增加对象池StringBuilderPool,减少对象分配
大石头 编写于 2018-06-12 14:28:17
X
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NewLife;
using NewLife.Collections;
using NewLife.Log;

namespace XCode.DataAccessLayer
{
    class SqlServer : RemoteDb
    {
        #region 属性
        /// <summary>返回数据库类型。外部DAL数据库类请使用Other</summary>
        public override DatabaseType Type => DatabaseType.SqlServer;

        private static DbProviderFactory _Factory;
        /// <summary>工厂</summary>
        public override DbProviderFactory Factory
        {
            get
            {
                if (_Factory == null)
                {
                    lock (typeof(SqlServer))
                    {
                        if (_Factory == null) _Factory = GetProviderFactory("System.Data.SqlClient.dll", "System.Data.SqlClient.SqlClientFactory");
                    }
                }

                return _Factory;
            }
        }

        /// <summary>是否SQL2012及以上</summary>
        public Boolean IsSQL2012 { get { return Version.Major > 11; } }

        private Version _Version;
        /// <summary>是否SQL2005及以上</summary>
        public Version Version
        {
            get
            {
                if (_Version == null)
                {
                    _Version = new Version(ServerVersion);

                    //var session = CreateSession();
                    //try
                    //{
                    //    // 取数据库版本
                    //    if (!session.Opened) session.Open();
                    //    var ver = session.Conn.ServerVersion;
                    //    session.AutoClose();

                    //    _Version = new Version(ver);
                    //}
                    //catch (Exception ex)
                    //{
                    //    XTrace.WriteLine("查询[{0}]的版本时出错,将按MSSQL2000进行分页处理!{1}", ConnName, ex);
                    //    _Version = new Version();
                    //}
                    //finally { session.Dispose(); }
                }
                return _Version;
            }
        }

        /// <summary>数据目录</summary>
        public String DataPath { get; set; }

        const String Application_Name = "Application Name";
        protected override void OnSetConnectionString(ConnectionStringBuilder builder)
        {
            // 获取数据目录,用于反向工程创建数据库
            if (builder.TryGetAndRemove("DataPath", out var str) && !str.IsNullOrEmpty()) DataPath = str;

            base.OnSetConnectionString(builder);

            if (builder[Application_Name] == null)
            {
#if !__CORE__
                var name = Runtime.IsWeb ? System.Web.Hosting.HostingEnvironment.SiteName : AppDomain.CurrentDomain.FriendlyName;
#else
                var name = AppDomain.CurrentDomain.FriendlyName;
#endif
                builder[Application_Name] = String.Format("XCode_{0}_{1}", name, ConnName);
            }
        }
        #endregion

        #region 方法
        /// <summary>创建数据库会话</summary>
        /// <returns></returns>
        protected override IDbSession OnCreateSession() { return new SqlServerSession(this); }

        /// <summary>创建元数据对象</summary>
        /// <returns></returns>
        protected override IMetaData OnCreateMetaData() { return new SqlServerMetaData(); }

        public override Boolean Support(String providerName)
        {
            providerName = providerName.ToLower();
            if (providerName.Contains("system.data.sqlclient")) return true;
            if (providerName.Contains("sql2012")) return true;
            if (providerName.Contains("sql2008")) return true;
            if (providerName.Contains("sql2005")) return true;
            if (providerName.Contains("sql2000")) return true;
            if (providerName == "sqlclient") return true;
            if (providerName.Contains("mssql")) return true;
            if (providerName.Contains("sqlserver")) return true;

            return false;
        }
        #endregion

        #region 分页
        /// <summary>构造分页SQL</summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <param name="keyColumn">唯一键。用于not in分页</param>
        /// <returns>分页SQL</returns>
        public override String PageSplit(String sql, Int64 startRowIndex, Int64 maximumRows, String keyColumn)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0 && maximumRows < 1) return sql;

            if (startRowIndex > 0)
            {
                // 指定了起始行,并且是SQL2005及以上版本,使用MS SQL 2012特有的分页算法
                if (IsSQL2012)
                {
                    // 从第一行开始,不需要分页
                    if (startRowIndex <= 0)
                    {
                        if (maximumRows < 1) return sql;

                        var sql_ = FormatSqlserver2012SQL(sql);
                        return $"{sql_} offset 1 rows fetch next {maximumRows} rows only ";
                    }
                    if (maximumRows < 1) throw new NotSupportedException("不支持取第几条数据之后的所有数据!");

                    var sql__ = FormatSqlserver2012SQL(sql);
                    return $"{sql__} offset {startRowIndex} rows fetch next {maximumRows} rows only ";
                }

                // 指定了起始行,并且是SQL2005及以上版本,使用RowNumber算法
                //if (IsSQL2005)
                {
                    //return PageSplitRowNumber(sql, startRowIndex, maximumRows, keyColumn);
                    var builder = new SelectBuilder();
                    builder.Parse(sql);
                    //return MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, IsSQL2005).ToString();

                    return PageSplit(builder, startRowIndex, maximumRows).ToString();
                }
            }

            // 如果没有Order By,直接调用基类方法
            // 先用字符串判断,命中率高,这样可以提高处理效率
            if (!sql.Contains(" Order "))
            {
                if (!sql.ToLower().Contains(" order ")) return base.PageSplit(sql, startRowIndex, maximumRows, keyColumn);
            }
            //// 使用正则进行严格判断。必须包含Order By,并且它右边没有右括号),表明有order by,且不是子查询的,才需要特殊处理
            //MatchCollection ms = Regex.Matches(sql, @"\border\s*by\b([^)]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            //if (ms == null || ms.Count < 1 || ms[0].Index < 1)
            var sql2 = sql;
            var orderBy = CheckOrderClause(ref sql2);
            if (String.IsNullOrEmpty(orderBy))
            {
                return base.PageSplit(sql, startRowIndex, maximumRows, keyColumn);
            }
            // 已确定该sql最外层含有order by,再检查最外层是否有top。因为没有top的order by是不允许作为子查询的
            if (Regex.IsMatch(sql, @"^[^(]+\btop\b", RegexOptions.Compiled | RegexOptions.IgnoreCase))
            {
                return base.PageSplit(sql, startRowIndex, maximumRows, keyColumn);
            }
            //String orderBy = sql.Substring(ms[0].Index);

            // 从第一行开始,不需要分页
            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                    return sql;
                else
                    return String.Format("Select Top {0} * From {1} {2}", maximumRows, CheckSimpleSQL(sql2), orderBy);
                //return String.Format("Select Top {0} * From {1} {2}", maximumRows, CheckSimpleSQL(sql.Substring(0, ms[0].Index)), orderBy);
            }

            #region Max/Min分页
            // 如果要使用max/min分页法,首先keyColumn必须有asc或者desc
            var kc = keyColumn.ToLower();
            if (kc.EndsWith(" desc") || kc.EndsWith(" asc") || kc.EndsWith(" unknown"))
            {
                var str = PageSplitMaxMin(sql, startRowIndex, maximumRows, keyColumn);
                if (!String.IsNullOrEmpty(str)) return str;
                keyColumn = keyColumn.Substring(0, keyColumn.IndexOf(" "));
            }
            #endregion

            sql = CheckSimpleSQL(sql2);

            if (String.IsNullOrEmpty(keyColumn)) throw new ArgumentNullException("keyColumn", "分页要求指定主键列或者排序字段!");

            if (maximumRows < 1)
                sql = String.Format("Select * From {1} Where {2} Not In(Select Top {0} {2} From {1} {3}) {3}", startRowIndex, sql, keyColumn, orderBy);
            else
                sql = String.Format("Select Top {0} * From {1} Where {2} Not In(Select Top {3} {2} From {1} {4}) {4}", maximumRows, sql, keyColumn, startRowIndex, orderBy);
            return sql;
        }

        /// <summary>
        /// 格式化SQL SERVER 2012分页前半部分SQL语句
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        private String FormatSqlserver2012SQL(String sql)
        {
            var builder = new SelectBuilder();
            builder.Parse(sql);

            var sb = NewLife.Collections.Pool.StringBuilder.Get();
            sb.Append("Select ");
            sb.Append(builder.ColumnOrDefault);
            sb.Append(" From ");
            sb.Append(builder.Table);
            if (!String.IsNullOrEmpty(builder.Where))
            {
                sb.Append(" Where type='p' and " + builder.Where);
            }
            else
            {
                sb.Append(" Where type='p' ");
            }
            if (!String.IsNullOrEmpty(builder.GroupBy)) sb.Append(" Group By " + builder.GroupBy);
            if (!String.IsNullOrEmpty(builder.Having)) sb.Append(" Having " + builder.Having);
            if (!String.IsNullOrEmpty(builder.OrderBy)) sb.Append(" Order By " + builder.OrderBy);

            return sb.Put(true);
        }

        public override SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
        {
            //return MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, IsSQL2005, b => CreateSession().QueryCount(b));

            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                {
                    return builder;
                }
                else if (builder.KeyIsOrderBy)
                {
                    return builder.Clone().Top(maximumRows);
                }
            }

            if (builder.Keys == null || builder.Keys.Length < 1) throw new XCodeException("分页算法要求指定排序列!" + builder.ToString());
            // 如果包含分组,则必须作为子查询
            var builder1 = builder.CloneWithGroupBy("XCode_T0", true);
            //builder1.Column = String.Format("{0}, row_number() over(Order By {1}) as rowNumber", builder.ColumnOrDefault, builder.OrderBy ?? builder.KeyOrder);
            // 不必追求极致,把所有列放出来
            builder1.Column = "*, row_number() over(Order By {0}) as rowNumber".F(builder.OrderBy ?? builder.KeyOrder);

            var builder2 = builder1.AsChild("XCode_T1", true);
            // 结果列处理
            //builder2.Column = builder.Column;
            //// 如果结果列包含有“.”,即有形如tab1.id、tab2.name之类的列时设为获取子查询的全部列
            //if ((!string.IsNullOrEmpty(builder2.Column)) && builder2.Column.Contains("."))
            //{
            //    builder2.Column = "*";
            //}
            // 不必追求极致,把所有列放出来
            builder2.Column = "*";

            // row_number()直接影响了排序,这里不再需要
            builder2.OrderBy = null;
            if (maximumRows < 1)
                builder2.Where = String.Format("rowNumber>={0}", startRowIndex + 1);
            else
                builder2.Where = String.Format("rowNumber Between {0} And {1}", startRowIndex + 1, startRowIndex + maximumRows);

            return builder2;
        }
        #endregion

        #region 数据库特性
        protected override String ReservedWordsStr
        {
            get
            {
                return "ADD,EXCEPT,PERCENT,ALL,EXEC,PLAN,ALTER,EXECUTE,PRECISION,AND,EXISTS,PRIMARY,ANY,EXIT,PRINT,AS,FETCH,PROC,ASC,FILE,PROCEDURE,AUTHORIZATION,FILLFACTOR,PUBLIC,BACKUP,FOR,RAISERROR,BEGIN,FOREIGN,READ,BETWEEN,FREETEXT,READTEXT,BREAK,FREETEXTTABLE,RECONFIGURE,BROWSE,FROM,REFERENCES,BULK,FULL,REPLICATION,BY,FUNCTION,RESTORE,CASCADE,GOTO,RESTRICT,CASE,GRANT,RETURN,CHECK,GROUP,REVOKE,CHECKPOINT,HAVING,RIGHT,CLOSE,HOLDLOCK,ROLLBACK,CLUSTERED,IDENTITY,ROWCOUNT,COALESCE,IDENTITY_INSERT,ROWGUIDCOL,COLLATE,IDENTITYCOL,RULE,COLUMN,IF,SAVE,COMMIT,IN,SCHEMA,COMPUTE,INDEX,SELECT,CONSTRAINT,INNER,SESSION_USER,CONTAINS,INSERT,SET,CONTAINSTABLE,INTERSECT,SETUSER,CONTINUE,INTO,SHUTDOWN,CONVERT,IS,SOME,CREATE,JOIN,STATISTICS,CROSS,KEY,SYSTEM_USER,CURRENT,KILL,TABLE,CURRENT_DATE,LEFT,TEXTSIZE,CURRENT_TIME,LIKE,THEN,CURRENT_TIMESTAMP,LINENO,TO,CURRENT_USER,LOAD,TOP,CURSOR,NATIONAL ,TRAN,DATABASE,NOCHECK,TRANSACTION,DBCC,NONCLUSTERED,TRIGGER,DEALLOCATE,NOT,TRUNCATE,DECLARE,NULL,TSEQUAL,DEFAULT,NULLIF,UNION,DELETE,OF,UNIQUE,DENY,OFF,UPDATE,DESC,OFFSETS,UPDATETEXT,DISK,ON,USE,DISTINCT,OPEN,USER,DISTRIBUTED,OPENDATASOURCE,VALUES,DOUBLE,OPENQUERY,VARYING,DROP,OPENROWSET,VIEW,DUMMY,OPENXML,WAITFOR,DUMP,OPTION,WHEN,ELSE,OR,WHERE,END,ORDER,WHILE,ERRLVL,OUTER,WITH,ESCAPE,OVER,WRITETEXT,ABSOLUTE,FOUND,PRESERVE,ACTION,FREE,PRIOR,ADMIN,GENERAL,PRIVILEGES,AFTER,GET,READS,AGGREGATE,GLOBAL,REAL,ALIAS,GO,RECURSIVE,ALLOCATE,GROUPING,REF,ARE,HOST,REFERENCING,ARRAY,HOUR,RELATIVE,ASSERTION,IGNORE,RESULT,AT,IMMEDIATE,RETURNS,BEFORE,INDICATOR,ROLE,BINARY,INITIALIZE,ROLLUP,BIT,INITIALLY,ROUTINE,BLOB,INOUT,ROW,BOOLEAN,INPUT,ROWS,BOTH,INT,SAVEPOINT,BREADTH,INTEGER,SCROLL,CALL,INTERVAL,SCOPE,CASCADED,ISOLATION,SEARCH,CAST,ITERATE,SECOND,CATALOG,LANGUAGE,SECTION,CHAR,LARGE,SEQUENCE,CHARACTER,LAST,SESSION,CLASS,LATERAL,SETS,CLOB,LEADING,SIZE,COLLATION,LESS,SMALLINT,COMPLETION,LEVEL,SPACE,CONNECT,LIMIT,SPECIFIC,CONNECTION,LOCAL,SPECIFICTYPE,CONSTRAINTS,LOCALTIME,SQL,CONSTRUCTOR,LOCALTIMESTAMP,SQLEXCEPTION,CORRESPONDING,LOCATOR,SQLSTATE,CUBE,MAP,SQLWARNING,CURRENT_PATH,MATCH,START,CURRENT_ROLE,MINUTE,STATE,CYCLE,MODIFIES,STATEMENT,DATA,MODIFY,STATIC,DATE,MODULE,STRUCTURE,DAY,MONTH,TEMPORARY,DEC,NAMES,TERMINATE,DECIMAL,NATURAL,THAN,DEFERRABLE,NCHAR,TIME,DEFERRED,NCLOB,TIMESTAMP,DEPTH,NEW,TIMEZONE_HOUR,DEREF,NEXT,TIMEZONE_MINUTE,DESCRIBE,NO,TRAILING,DESCRIPTOR,NONE,TRANSLATION,DESTROY,NUMERIC,TREAT,DESTRUCTOR,OBJECT,TRUE,DETERMINISTIC,OLD,UNDER,DICTIONARY,ONLY,UNKNOWN,DIAGNOSTICS,OPERATION,UNNEST,DISCONNECT,ORDINALITY,USAGE,DOMAIN,OUT,USING,DYNAMIC,OUTPUT,VALUE,EACH,PAD,VARCHAR,END-EXEC,PARAMETER,VARIABLE,EQUALS,PARAMETERS,WHENEVER,EVERY,PARTIAL,WITHOUT,EXCEPTION,PATH,WORK,EXTERNAL,POSTFIX,WRITE,FALSE,PREFIX,YEAR,FIRST,PREORDER,ZONE,FLOAT,PREPARE,ADA,AVG,BIT_LENGTH,CHAR_LENGTH,CHARACTER_LENGTH,COUNT,EXTRACT,FORTRAN,INCLUDE,INSENSITIVE,LOWER,MAX,MIN,OCTET_LENGTH,OVERLAPS,PASCAL,POSITION,SQLCA,SQLCODE,SQLERROR,SUBSTRING,SUM,TRANSLATE,TRIM,UPPER," +
                  "Sort,Level,User,Online";
            }
        }

        /// <summary>长文本长度</summary>
        public override Int32 LongTextLength => 4000;

        /// <summary>格式化时间为SQL字符串</summary>
        /// <param name="dateTime">时间值</param>
        /// <returns></returns>
        public override String FormatDateTime(DateTime dateTime) => "{ts'" + dateTime.ToFullString() + "'}";

        /// <summary>格式化名称,如果是关键字,则格式化后返回,否则原样返回</summary>
        /// <param name="name">名称</param>
        /// <returns></returns>
        public override String FormatName(String name)
        {
            // SqlServer数据库名和表名可以用横线。。。
            if (name.Contains("-")) return "[{0}]".F(name);

            return base.FormatName(name);
        }

        /// <summary>格式化关键字</summary>
        /// <param name="keyWord">关键字</param>
        /// <returns></returns>
        public override String FormatKeyWord(String keyWord)
        {
            //if (String.IsNullOrEmpty(keyWord)) throw new ArgumentNullException("keyWord");
            if (String.IsNullOrEmpty(keyWord)) return keyWord;

            if (keyWord.StartsWith("[") && keyWord.EndsWith("]")) return keyWord;

            return String.Format("[{0}]", keyWord);
        }

        /// <summary>系统数据库名</summary>
        public override String SystemDatabaseName { get { return "master"; } }

        public override String FormatValue(IDataColumn field, Object value)
        {
            var code = System.Type.GetTypeCode(field.DataType);
            var isNullable = field.Nullable;

            if (code == TypeCode.String)
            {
                // 热心网友 Hannibal 在处理日文网站时发现插入的日文为乱码,这里加上N前缀
                if (value == null) return isNullable ? "null" : "''";

                // 为了兼容旧版本实体类
                if (field.RawType.StartsWithIgnoreCase("n"))
                    return "N'" + value.ToString().Replace("'", "''") + "'";
                else
                    return "'" + value.ToString().Replace("'", "''") + "'";
            }

            return base.FormatValue(field, value);
        }
        #endregion
    }

    /// <summary>SqlServer数据库</summary>
    internal class SqlServerSession : RemoteDbSession
    {
        #region 构造函数
        public SqlServerSession(IDatabase db) : base(db) { }
        #endregion

        #region 查询
        /// <summary>快速查询单表记录数,稍有偏差</summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public override Int64 QueryCountFast(String tableName)
        {
            tableName = tableName.Trim().Trim('[', ']').Trim();

            //var n = 0L;
            //if (QueryIndex().TryGetValue(tableName, out n)) return n;

            var sql = String.Format("select rows from sysindexes where id = object_id('{0}') and indid in (0,1)", tableName);
            return ExecuteScalar<Int64>(sql);
        }

        //Dictionary<String, Int64> _index;
        //DateTime _next;

        //Dictionary<String, Int64> QueryIndex()
        //{
        //    // 检查更新
        //    if (_index == null || _next < DateTime.Now)
        //    {
        //        _index = QueryIndex_();
        //        _next = DateTime.Now.AddSeconds(10);
        //    }

        //    return _index;
        //}

        //Dictionary<String, Int64> QueryIndex_()
        //{
        //    var ds = Query("select object_name(id) as objname,rows from sysindexes where indid in (0,1) and status in (0,2066)");
        //    var dic = new Dictionary<String, Int64>(StringComparer.OrdinalIgnoreCase);
        //    foreach (DataRow dr in ds.Tables[0].Rows)
        //    {
        //        dic.Add(dr[0] + "", (Int32)dr[1]);
        //    }
        //    return dic;
        //}

        /// <summary>执行插入语句并返回新增行的自动编号</summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="type">命令类型,默认SQL文本</param>
        /// <param name="ps">命令参数</param>
        /// <returns>新增行的自动编号</returns>
        public override Int64 InsertAndGetIdentity(String sql, CommandType type = CommandType.Text, params IDataParameter[] ps)
        {
            sql = "SET NOCOUNT ON;" + sql + ";Select SCOPE_IDENTITY()";
            return base.InsertAndGetIdentity(sql, type, ps);
        }
        #endregion
    }

    /// <summary>SqlServer元数据</summary>
    class SqlServerMetaData : RemoteDbMetaData
    {
        public SqlServerMetaData()
        {
            Types = _DataTypes;
        }

        #region 属性
        ///// <summary>是否SQL2005</summary>
        //public Boolean IsSQL2005 { get { return (Database as SqlServer).IsSQL2005; } }

        public Version Version { get { return (Database as SqlServer).Version; } }

        ///// <summary>0级类型</summary>
        //public String Level0type { get { return IsSQL2005 ? "SCHEMA" : "USER"; } }
        #endregion

        #region 构架
        /// <summary>取得所有表构架</summary>
        /// <returns></returns>
        protected override List<IDataTable> OnGetTables(String[] names)
        {
            #region 查表说明、字段信息、索引信息
            var session = Database.CreateSession();

            //一次性把所有的表说明查出来
            DataTable DescriptionTable = null;

            //var old = session.ShowSQL;
            //session.ShowSQL = false;
            try
            {
                var sql = "select b.name n, a.value v from sys.extended_properties a inner join sysobjects b on a.major_id=b.id and a.minor_id=0 and a.name = 'MS_Description'";
                DescriptionTable = session.Query(sql).Tables[0];
            }
            catch (Exception ex) { XTrace.WriteException(ex); }
            //session.ShowSQL = old;

            var dt = GetSchema(_.Tables, null);
            if (dt == null || dt.Rows == null || dt.Rows.Count < 1) return null;

            //session.ShowSQL = false;
            try
            {
                AllFields = session.Query(SchemaSql).Tables[0];
                AllIndexes = session.Query(IndexSql).Tables[0];
            }
            catch (Exception ex) { XTrace.WriteException(ex); }
            //session.ShowSQL = old;
            #endregion

            // 列出用户表
            var rows = dt.Select(String.Format("({0}='BASE TABLE' Or {0}='VIEW') AND TABLE_NAME<>'Sysdiagrams'", "TABLE_TYPE"));
            if (rows == null || rows.Length < 1) return null;

            var list = GetTables(rows, names);
            if (list == null || list.Count < 1) return list;

            // 修正备注
            foreach (var item in list)
            {
                var drs = DescriptionTable?.Select("n='" + item.TableName + "'");
                item.Description = drs == null || drs.Length < 1 ? "" : drs[0][1].ToString();
            }

            return list;
        }

        private DataTable AllFields = null;
        private DataTable AllIndexes = null;

        protected override void FixField(IDataColumn field, DataRow dr)
        {
            base.FixField(field, dr);

            var rows = AllFields?.Select("表名='" + field.Table.TableName + "' And 字段名='" + field.ColumnName + "'", null);
            if (rows != null && rows.Length > 0)
            {
                var dr2 = rows[0];

                field.Identity = GetDataRowValue<Boolean>(dr2, "标识");
                field.PrimaryKey = GetDataRowValue<Boolean>(dr2, "主键");
                //field.NumOfByte = GetDataRowValue<Int32>(dr2, "占用字节数");
                field.Description = GetDataRowValue<String>(dr2, "字段说明");
                //field.Precision = GetDataRowValue<Int32>(dr2, "精度");
                //field.Scale = GetDataRowValue<Int32>(dr2, "小数位数");
            }
        }

        protected override List<IDataIndex> GetIndexes(IDataTable table, DataTable _indexes, DataTable _indexColumns)
        {
            var list = base.GetIndexes(table, _indexes, _indexColumns);
            if (list != null && list.Count > 0)
            {
                foreach (var item in list)
                {
                    var drs = AllIndexes?.Select("name='" + item.Name + "'");
                    if (drs != null && drs.Length > 0)
                    {
                        item.Unique = GetDataRowValue<Boolean>(drs[0], "is_unique");
                        item.PrimaryKey = GetDataRowValue<Boolean>(drs[0], "is_primary_key");
                    }
                }
            }
            return list;
        }

        public override String CreateTableSQL(IDataTable table)
        {
            var sql = base.CreateTableSQL(table);

            var pks = table.PrimaryKeys;
            if (String.IsNullOrEmpty(sql) || pks == null || pks.Length < 2) return sql;

            // 处理多主键
            var sb = new StringBuilder();
            foreach (var item in pks)
            {
                if (sb.Length > 0) sb.Append(",");
                sb.Append(FormatName(item.ColumnName));
            }
            sql += "; " + Environment.NewLine;
            sql += String.Format("Alter Table {0} Add Constraint PK_{1} Primary Key Clustered({2})", FormatName(table.TableName), table.TableName, sb.ToString());
            return sql;
        }

        public override String FieldClause(IDataColumn field, Boolean onlyDefine)
        {
            if (!String.IsNullOrEmpty(field.RawType) && field.RawType.Contains("char(-1)"))
            {
                //if (IsSQL2005)
                field.RawType = field.RawType.Replace("char(-1)", "char(MAX)");
                //else
                //    field.RawType = field.RawType.Replace("char(-1)", "char(" + (Int32.MaxValue / 2) + ")");
            }

            ////chenqi 2017-3-28
            ////增加处理decimal类型精度和小数位数处理
            ////此处只针对Sql server进行处理
            ////严格来说,应该修改的地方是
            //if (!String.IsNullOrEmpty(field.RawType) && field.RawType.Contains("decimal"))
            //{
            //    field.RawType = $"decimal({field.Precision},{field.Scale})";
            //}

            return base.FieldClause(field, onlyDefine);
        }

        protected override String GetFieldConstraints(IDataColumn field, Boolean onlyDefine)
        {
            // 非定义时(修改字段),主键字段没有约束
            if (!onlyDefine && field.PrimaryKey) return null;

            var str = base.GetFieldConstraints(field, onlyDefine);

            // 非定义时,自增字段没有约束
            if (onlyDefine && field.Identity) str = " IDENTITY(1,1)" + str;

            return str;
        }

        //protected override String GetFormatParam(IDataColumn field, DataRow dr)
        //{
        //    var str = base.GetFormatParam(field, dr);
        //    if (String.IsNullOrEmpty(str)) return str;

        //    // 这个主要来自于float,因为无法取得其精度
        //    if (str == "(0)") return null;
        //    return str;
        //}

        //protected override String GetFormatParamItem(IDataColumn field, DataRow dr, String item)
        //{
        //    var pi = base.GetFormatParamItem(field, dr, item);
        //    if (field.DataType == typeof(String) && pi == "-1" && IsSQL2005) return "MAX";
        //    return pi;
        //}
        #endregion

        #region 取得字段信息的SQL模版
        private String _SchemaSql = "";
        /// <summary>构架SQL</summary>
        public virtual String SchemaSql
        {
            get
            {
                if (String.IsNullOrEmpty(_SchemaSql))
                {
                    var sb = new StringBuilder();
                    sb.Append("SELECT ");
                    sb.Append("表名=d.name,");
                    sb.Append("字段序号=a.colorder,");
                    sb.Append("字段名=a.name,");
                    sb.Append("标识=case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then Convert(Bit,1) else Convert(Bit,0) end,");
                    sb.Append("主键=case when exists(SELECT 1 FROM sysobjects where xtype='PK' and name in (");
                    sb.Append("SELECT name FROM sysindexes WHERE id = a.id AND indid in(");
                    sb.Append("SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid");
                    sb.Append("))) then Convert(Bit,1) else Convert(Bit,0) end,");
                    sb.Append("类型=b.name,");
                    sb.Append("占用字节数=a.length,");
                    sb.Append("长度=COLUMNPROPERTY(a.id,a.name,'PRECISION'),");
                    sb.Append("小数位数=isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0),");
                    sb.Append("允许空=case when a.isnullable=1 then Convert(Bit,1)else Convert(Bit,0) end,");
                    sb.Append("默认值=isnull(e.text,''),");
                    sb.Append("字段说明=isnull(g.[value],'')");
                    sb.Append("FROM syscolumns a ");
                    sb.Append("left join systypes b on a.xtype=b.xusertype ");
                    sb.Append("inner join sysobjects d on a.id=d.id  and d.xtype='U' ");
                    sb.Append("left join syscomments e on a.cdefault=e.id ");
                    //if (IsSQL2005)
                    //{
                    sb.Append("left join sys.extended_properties g on a.id=g.major_id and a.colid=g.minor_id and g.name = 'MS_Description'  ");
                    //}
                    //else
                    //{
                    //    sb.Append("left join sysproperties g on a.id=g.id and a.colid=g.smallid  ");
                    //}
                    sb.Append("order by a.id,a.colorder");
                    _SchemaSql = sb.ToString();
                }
                return _SchemaSql;
            }
        }

        private String _IndexSql;
        public virtual String IndexSql
        {
            get
            {
                if (_IndexSql == null)
                {
                    //if (IsSQL2005)
                    _IndexSql = "select ind.* from sys.indexes ind inner join sys.objects obj on ind.object_id = obj.object_id where obj.type='u'";
                    //else
                    //    _IndexSql = "select IndexProperty(obj.id, ind.name,'IsUnique') as is_unique, ObjectProperty(object_id(ind.name),'IsPrimaryKey') as is_primary_key,ind.* from sysindexes ind inner join sysobjects obj on ind.id = obj.id where obj.type='u'";
                }
                return _IndexSql;
            }
        }

        //private readonly String _DescriptionSql2000 = "select b.name n, a.value v from sysproperties a inner join sysobjects b on a.id=b.id where a.smallid=0";
        //private readonly String _DescriptionSql2005 = "select b.name n, a.value v from sys.extended_properties a inner join sysobjects b on a.major_id=b.id and a.minor_id=0 and a.name = 'MS_Description'";
        ///// <summary>取表说明SQL</summary>
        //public virtual String DescriptionSql { get { return IsSQL2005 ? _DescriptionSql2005 : _DescriptionSql2000; } }
        #endregion

        #region 数据定义
        public override String CreateDatabaseSQL(String dbname, String file)
        {
            var dp = (Database as SqlServer).DataPath;

            if (String.IsNullOrEmpty(file))
            {
                if (String.IsNullOrEmpty(dp)) return String.Format("CREATE DATABASE [{0}]", FormatName(dbname));

                file = dbname + ".mdf";
            }

            var logfile = String.Empty;

            if (!Path.IsPathRooted(file))
            {
                if (!String.IsNullOrEmpty(dp)) file = Path.Combine(dp, file);

                if (!Path.IsPathRooted(file)) file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file);
            }
            if (String.IsNullOrEmpty(Path.GetExtension(file))) file += ".mdf";
            file = new FileInfo(file).FullName;

            logfile = Path.ChangeExtension(file, ".ldf");
            logfile = new FileInfo(logfile).FullName;

            var dir = Path.GetDirectoryName(file);
            if (!String.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir);

            var sb = new StringBuilder();

            sb.AppendFormat("CREATE DATABASE {0} ON  PRIMARY", FormatName(dbname));
            sb.AppendLine();
            sb.AppendFormat(@"( NAME = N'{0}', FILENAME = N'{1}', SIZE = 10 , MAXSIZE = UNLIMITED, FILEGROWTH = 10%)", dbname, file);
            sb.AppendLine();
            sb.Append("LOG ON ");
            sb.AppendLine();
            sb.AppendFormat(@"( NAME = N'{0}_Log', FILENAME = N'{1}', SIZE = 10 , MAXSIZE = UNLIMITED, FILEGROWTH = 10%)", dbname, logfile);
            sb.AppendLine();

            return sb.ToString();
        }

        public override String DatabaseExistSQL(String dbname)
        {
            return String.Format("SELECT * FROM sysdatabases WHERE name = N'{0}'", dbname);
        }

        /// <summary>使用数据架构确定数据库是否存在,因为使用系统视图可能没有权限</summary>
        /// <param name="dbname"></param>
        /// <returns></returns>
        protected override Boolean DatabaseExist(String dbname)
        {
            var dt = GetSchema(_.Databases, new String[] { dbname });
            return dt != null && dt.Rows != null && dt.Rows.Count > 0;
        }

        protected override Boolean DropDatabase(String databaseName)
        {
            //return base.DropDatabase(databaseName);

            // SQL语句片段,断开该数据库所有链接
            var sb = new StringBuilder();
            sb.AppendLine("use master");
            sb.AppendLine(";");
            sb.AppendLine("declare   @spid   varchar(20),@dbname   varchar(20)");
            sb.AppendLine("declare   #spid   cursor   for");
            sb.AppendFormat("select   spid=cast(spid   as   varchar(20))   from   master..sysprocesses   where   dbid=db_id('{0}')", databaseName);
            sb.AppendLine();
            sb.AppendLine("open   #spid");
            sb.AppendLine("fetch   next   from   #spid   into   @spid");
            sb.AppendLine("while   @@fetch_status=0");
            sb.AppendLine("begin");
            sb.AppendLine("exec('kill   '+@spid)");
            sb.AppendLine("fetch   next   from   #spid   into   @spid");
            sb.AppendLine("end");
            sb.AppendLine("close   #spid");
            sb.AppendLine("deallocate   #spid");

            var count = 0;
            var session = Database.CreateSession();
            try
            {
                count = session.Execute(sb.ToString());
            }
            catch (Exception ex) { XTrace.WriteException(ex); }
            return session.Execute(String.Format("Drop Database {0}", FormatName(databaseName))) > 0;
        }

        public override String TableExistSQL(String tableName)
        {
            //if (IsSQL2005)
            return String.Format("select * from sysobjects where xtype='U' and name='{0}'", tableName);
            //else
            //    return String.Format("SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[dbo].{0}') AND OBJECTPROPERTY(id, N'IsUserTable') = 1", FormatName(tableName));
        }

        /// <summary>使用数据架构确定数据表是否存在,因为使用系统视图可能没有权限</summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public Boolean TableExist(IDataTable table)
        {
            var dt = GetSchema(_.Tables, new String[] { null, null, table.TableName, null });
            return dt != null && dt.Rows != null && dt.Rows.Count > 0;
        }

        protected override String RenameTable(String tableName, String tempTableName)
        {
            if (Version.Major >= 8)
                return String.Format("EXECUTE sp_rename N'{0}', N'{1}', 'OBJECT' ", tableName, tempTableName);
            else
                return base.RenameTable(tableName, tempTableName);
        }

        protected override String ReBuildTable(IDataTable entitytable, IDataTable dbtable)
        {
            var sql = base.ReBuildTable(entitytable, dbtable);
            if (String.IsNullOrEmpty(sql)) return sql;

            // 特殊处理带标识列的表,需要增加SET IDENTITY_INSERT
            if (!entitytable.Columns.Any(e => e.Identity)) return sql;

            var tableName = Database.FormatName(entitytable.TableName);
            var ss = sql.Split("; " + Environment.NewLine);
            for (var i = 0; i < ss.Length; i++)
            {
                if (ss[i].StartsWithIgnoreCase("Insert Into"))
                {
                    ss[i] = String.Format("SET IDENTITY_INSERT {1} ON;{0};SET IDENTITY_INSERT {1} OFF", ss[i], tableName);
                    break;
                }
            }
            return String.Join("; " + Environment.NewLine, ss);
        }

        public override String AddTableDescriptionSQL(IDataTable table)
        {
            return String.Format("EXEC dbo.sp_addextendedproperty @name=N'MS_Description', @value=N'{1}' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{0}'", table.TableName, table.Description);
        }

        public override String DropTableDescriptionSQL(IDataTable table)
        {
            return String.Format("EXEC dbo.sp_dropextendedproperty @name=N'MS_Description', @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{0}'", table.TableName);
        }

        public override String AddColumnSQL(IDataColumn field)
        {
            return String.Format("Alter Table {0} Add {1}", FormatName(field.Table.TableName), FieldClause(field, true));
        }

        public override String AlterColumnSQL(IDataColumn field, IDataColumn oldfield)
        {
            // 创建为自增,重建表
            if (field.Identity && !oldfield.Identity)
            {
                //return DropColumnSQL(oldfield) + ";" + Environment.NewLine + AddColumnSQL(field);
                return ReBuildTable(field.Table, oldfield.Table);
            }
            // 类型改变,必须重建表
            if (IsColumnTypeChanged(field, oldfield)) return ReBuildTable(field.Table, oldfield.Table);

            var sql = String.Format("Alter Table {0} Alter Column {1}", FormatName(field.Table.TableName), FieldClause(field, false));
            var pk = DeletePrimaryKeySQL(field);
            if (field.PrimaryKey)
            {
                // 如果没有主键删除脚本,表明没有主键
                //if (String.IsNullOrEmpty(pk))
                if (!oldfield.PrimaryKey)
                {
                    // 增加主键约束
                    pk = String.Format("Alter Table {0} ADD CONSTRAINT PK_{0} PRIMARY KEY {2}({1}) ON [PRIMARY]", FormatName(field.Table.TableName), FormatName(field.ColumnName), field.Identity ? "CLUSTERED" : "");
                    sql += ";" + Environment.NewLine + pk;
                }
            }
            else
            {
                // 字段声明没有主键,但是主键实际存在,则删除主键
                //if (!String.IsNullOrEmpty(pk))
                if (oldfield.PrimaryKey)
                {
                    sql += ";" + Environment.NewLine + pk;
                }
            }

            //// 需要提前删除相关默认值
            //if (oldfield.Default != null)
            //{
            //    var df = DropDefaultSQL(oldfield);
            //    if (!String.IsNullOrEmpty(df))
            //    {
            //        sql = df + ";" + Environment.NewLine + sql;

            //        // 如果还有默认值,加上
            //        if (field.Default != null)
            //        {
            //            df = AddDefaultSQLWithNoCheck(field);
            //            if (!String.IsNullOrEmpty(df)) sql += ";" + Environment.NewLine + df;
            //        }
            //    }
            //}
            // 需要提前删除相关索引
            foreach (var di in oldfield.Table.Indexes)
            {
                // 如果包含该字段
                if (di.Columns.Contains(oldfield.ColumnName, StringComparer.OrdinalIgnoreCase))
                {
                    var dis = DropIndexSQL(di);
                    if (!String.IsNullOrEmpty(dis)) sql = dis + ";" + Environment.NewLine + sql;
                }
            }
            // 如果还有索引,则加上
            foreach (var di in field.Table.Indexes)
            {
                // 如果包含该字段
                if (di.Columns.Contains(field.ColumnName, StringComparer.OrdinalIgnoreCase))
                {
                    var cis = CreateIndexSQL(di);
                    if (!String.IsNullOrEmpty(cis)) sql += ";" + Environment.NewLine + cis;
                }
            }

            return sql;
        }

        public override String DropIndexSQL(IDataIndex index)
        {
            return String.Format("Drop Index {1}.{0}", FormatName(index.Name), FormatName(index.Table.TableName));
        }

        public override String DropColumnSQL(IDataColumn field)
        {
            ////删除默认值
            //String sql = DropDefaultSQL(field);
            //if (!String.IsNullOrEmpty(sql)) sql += ";" + Environment.NewLine;

            //删除主键
            var sql = DeletePrimaryKeySQL(field);
            if (!String.IsNullOrEmpty(sql)) sql += ";" + Environment.NewLine;

            sql += base.DropColumnSQL(field);
            return sql;
        }

        public override String AddColumnDescriptionSQL(IDataColumn field)
        {
            var sql = String.Format("EXEC dbo.sp_addextendedproperty @name=N'MS_Description', @value=N'{1}' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{0}', @level2type=N'COLUMN',@level2name=N'{2}'", field.Table.TableName, field.Description, field.ColumnName);
            return sql;
        }

        public override String DropColumnDescriptionSQL(IDataColumn field)
        {
            return String.Format("EXEC dbo.sp_dropextendedproperty @name=N'MS_Description', @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{0}', @level2type=N'COLUMN',@level2name=N'{1}'", field.Table.TableName, field.ColumnName);
        }

        String DeletePrimaryKeySQL(IDataColumn field)
        {
            if (!field.PrimaryKey) return String.Empty;

            var dis = field.Table.Indexes;
            if (dis == null || dis.Count < 1) return String.Empty;

            var di = dis.FirstOrDefault(e => e.Columns.Any(x => x.EqualIgnoreCase(field.ColumnName, field.Name)));
            if (di == null) return String.Empty;

            return String.Format("Alter Table {0} Drop CONSTRAINT {1}", FormatName(field.Table.TableName), di.Name);
        }

        public override String DropDatabaseSQL(String dbname)
        {
            var sb = new StringBuilder();
            sb.AppendLine("use master");
            sb.AppendLine(";");
            sb.AppendLine("declare   @spid   varchar(20),@dbname   varchar(20)");
            sb.AppendLine("declare   #spid   cursor   for");
            sb.AppendFormat("select   spid=cast(spid   as   varchar(20))   from   master..sysprocesses   where   dbid=db_id('{0}')", dbname);
            sb.AppendLine();
            sb.AppendLine("open   #spid");
            sb.AppendLine("fetch   next   from   #spid   into   @spid");
            sb.AppendLine("while   @@fetch_status=0");
            sb.AppendLine("begin");
            sb.AppendLine("exec('kill   '+@spid)");
            sb.AppendLine("fetch   next   from   #spid   into   @spid");
            sb.AppendLine("end");
            sb.AppendLine("close   #spid");
            sb.AppendLine("deallocate   #spid");
            sb.AppendLine(";");
            sb.AppendFormat("Drop Database {0}", FormatName(dbname));
            return sb.ToString();
        }
        #endregion

        /// <summary>数据类型映射</summary>
        private static Dictionary<Type, String[]> _DataTypes = new Dictionary<Type, String[]>
        {
            { typeof(Byte[]), new String[] { "binary({0})", "image", "varbinary({0})", "timestamp" } },
            //{ typeof(DateTimeOffset), new String[] { "datetimeoffset({0})" } },
            { typeof(Guid), new String[] { "uniqueidentifier" } },
            //{ typeof(Object), new String[] { "sql_variant" } },
            //{ typeof(TimeSpan), new String[] { "time({0})" } },
            { typeof(Boolean), new String[] { "bit" } },
            { typeof(Byte), new String[] { "tinyint" } },
            { typeof(Int16), new String[] { "smallint" } },
            { typeof(Int32), new String[] { "int" } },
            { typeof(Int64), new String[] { "bigint" } },
            { typeof(Single), new String[] { "real" } },
            { typeof(Double), new String[] { "float" } },
            { typeof(Decimal), new String[] { "money", "decimal({0}, {1})", "numeric({0}, {1})", "smallmoney" } },
            { typeof(DateTime), new String[] { "datetime", "smalldatetime", "datetime2({0})", "date" } },
            { typeof(String), new String[] { "nvarchar({0})", "ntext", "text", "varchar({0})", "char({0})", "nchar({0})", "xml" } }
        };

        #region 辅助函数
        /// <summary>除去字符串两端成对出现的符号</summary>
        /// <param name="str"></param>
        /// <param name="prefix"></param>
        /// <param name="suffix"></param>
        /// <returns></returns>
        public static String Trim(String str, String prefix, String suffix)
        {
            while (!String.IsNullOrEmpty(str))
            {
                if (!str.StartsWith(prefix)) return str;
                if (!str.EndsWith(suffix)) return str;

                str = str.Substring(prefix.Length, str.Length - suffix.Length - prefix.Length);
            }
            return str;
        }
        #endregion
    }
}