必须填写至少10个字的日志
nnhy authored at 2012-07-27 18:48:21
17.17 KiB
X
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading;
using NewLife.Reflection;
using System.Text;

namespace XCode.DataAccessLayer
{
    class SQLite : FileDbBase
    {
        #region 属性
        /// <summary>返回数据库类型。</summary>
        public override DatabaseType DbType { get { return DatabaseType.SQLite; } }

        private static DbProviderFactory _dbProviderFactory;
        /// <summary>提供者工厂</summary>
        static DbProviderFactory dbProviderFactory
        {
            get
            {
                if (_dbProviderFactory == null)
                {
                    lock (typeof(SQLite))
                    {
                        if (_dbProviderFactory == null) _dbProviderFactory = GetProviderFactory("System.Data.SQLite.dll", "System.Data.SQLite.SQLiteFactory");
                    }
                }

                return _dbProviderFactory;
            }
        }

        /// <summary>工厂</summary>
        public override DbProviderFactory Factory
        {
            get { return dbProviderFactory; }
        }

        /// <summary>是否内存数据库</summary>
        public Boolean IsMemoryDatabase { get { return String.Equals(FileName, MemoryDatabase, StringComparison.OrdinalIgnoreCase); } }

        static readonly String MemoryDatabase = ":memory:";

        protected override string OnResolveFile(string file)
        {
            if (String.IsNullOrEmpty(file) || String.Equals(file, MemoryDatabase, StringComparison.OrdinalIgnoreCase)) return MemoryDatabase;

            return base.OnResolveFile(file);
        }

        protected override void OnSetConnectionString(XDbConnectionStringBuilder builder)
        {
            base.OnSetConnectionString(builder);

            // 优化SQLite,如果原始字符串里面没有这些参数,就设置这些参数
            if (!builder.ContainsKey("Pooling")) builder["Pooling"] = "true";
            if (!builder.ContainsKey("Cache Size")) builder["Cache Size"] = "50000";
            if (!builder.ContainsKey("Synchronous")) builder["Synchronous"] = "Off";
            if (!builder.ContainsKey("Journal Mode")) builder["Journal Mode"] = "Memory";
        }
        #endregion

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

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

        #region 分页
        /// <summary>已重写。获取分页</summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <param name="keyColumn">主键列。用于not in分页</param>
        /// <returns></returns>
        public override string PageSplit(string sql, Int32 startRowIndex, Int32 maximumRows, string keyColumn)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                    return sql;
                else
                    return String.Format("{0} limit {1}", sql, maximumRows);
            }
            if (maximumRows < 1)
                throw new NotSupportedException("不支持取第几条数据之后的所有数据!");
            else
                sql = String.Format("{0} limit {1}, {2}", sql, startRowIndex, maximumRows);
            return sql;
        }
        #endregion

        #region 数据库特性
        /// <summary>当前时间函数</summary>
        public override String DateTimeNow { get { return "CURRENT_TIMESTAMP"; } }

        /// <summary>最小时间</summary>
        public override DateTime DateTimeMin { get { return DateTime.MinValue; } }

        /// <summary>获取Guid的函数,@老树 说SQLite没有这个函数</summary>
        public override String NewGuid { get { return null; } }

        /// <summary>格式化时间为SQL字符串</summary>
        /// <param name="dateTime">时间值</param>
        /// <returns></returns>
        public override String FormatDateTime(DateTime dateTime) { return String.Format("'{0:yyyy-MM-dd HH:mm:ss}'", dateTime); }

        /// <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);
            //return keyWord;
        }

        public override string FormatValue(IDataColumn field, object value)
        {
            if (field.DataType == typeof(Byte[]))
            {
                Byte[] bts = (Byte[])value;
                if (bts == null || bts.Length < 1) return "0x0";

                return "X'" + BitConverter.ToString(bts).Replace("-", null) + "'";
            }

            return base.FormatValue(field, value);
        }

        /// <summary>字符串相加</summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public override String StringConcat(String left, String right) { return (!String.IsNullOrEmpty(left) ? left : "\'\'") + "||" + (!String.IsNullOrEmpty(right) ? right : "\'\'"); }
        #endregion
    }

    /// <summary>SQLite数据库</summary>
    internal class SQLiteSession : FileDbSession
    {
        #region 方法
        protected override void CreateDatabase()
        {
            // 内存数据库不需要创建
            if ((Database as SQLite).IsMemoryDatabase) return;

            base.CreateDatabase();
        }
        #endregion

        #region 基本方法 查询/执行
        /// <summary>文件锁定重试次数</summary>
        const Int32 RetryTimes = 5;

        TResult Retry<TArg, TResult>(Func<TArg, TResult> func, TArg arg)
        {
            //! 如果异常是文件锁定,则重试
            for (int i = 0; i < RetryTimes; i++)
            {
                try
                {
                    return func(arg);
                }
                catch (Exception ex)
                {
                    if (i >= RetryTimes - 1) throw;

                    if (ex.Message == "The database file is locked")
                    {
                        Thread.Sleep(300);
                        continue;
                    }

                    throw;
                }
            }
            return default(TResult);
        }

        ///// <summary>
        ///// 已重载。增加锁
        ///// </summary>
        ///// <param name="sql">SQL语句</param>
        ///// <param name="type">命令类型,默认SQL文本</param>
        ///// <param name="ps">命令参数</param>
        ///// <returns></returns>
        //public override Int32 Execute(string sql, CommandType type = CommandType.Text, params DbParameter[] ps) { return Retry<String, Int32>(base.Execute, sql); }

        /// <summary>已重载。增加锁</summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public override Int32 Execute(DbCommand cmd) { return Retry<DbCommand, Int32>(base.Execute, cmd); }

        /// <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 DbParameter[] ps)
        {
            return Retry<String, Int64>(delegate(String sql2)
            {
                return ExecuteScalar<Int64>(sql2 + ";Select last_insert_rowid() newid", type, ps);
            }, sql);
        }
        #endregion
    }

    /// <summary>SQLite元数据</summary>
    class SQLiteMetaData : FileDbMetaData
    {
        #region 构架
        protected override List<IDataTable> OnGetTables(ICollection<String> names)
        {
            DataTable dt = GetSchema(_.Tables, null);
            if (dt == null || dt.Rows == null || dt.Rows.Count < 1) return null;

            // 默认列出所有字段
            DataRow[] rows = dt.Select("TABLE_TYPE='table'");
            rows = OnGetTables(names, rows);
            if (rows == null || rows.Length < 1) return null;

            return GetTables(rows);
        }

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

            // 如果数据库里面是integer或者autoincrement,识别类型是Int64,又是自增,则改为Int32,保持与大多数数据库的兼容
            if (field.Identity && field.DataType == typeof(Int64) && (field.RawType.EqualIgnoreCase("integer") || field.RawType.EqualIgnoreCase("autoincrement")))
            {
                field.DataType = typeof(Int32);
            }
        }

        protected override string GetFieldType(IDataColumn field)
        {
            String typeName = base.GetFieldType(field);

            // 自增字段必须是integer
            if (field.Identity && typeName == "int") return "integer";

            return typeName;
        }

        protected override DataRow[] FindDataType(IDataColumn field, string typeName, bool? isLong)
        {
            DataRow[] drs = base.FindDataType(field, typeName, isLong);
            if (drs == null || drs.Length < 1)
            {
                // 字符串
                if (typeName.IndexOf("int", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    var name = typeName.ToLower();
                    if (name == "int16")
                        name = "smallint";
                    else if (name == "int32")
                        name = "int";
                    else if (name == "int64")
                        name = "bigint";

                    if (name != typeName.ToLower()) return base.FindDataType(field, name, isLong);
                }
            }
            return drs;
        }
        protected override string GetFieldConstraints(IDataColumn field, Boolean onlyDefine)
        {
            String str = null;

            //Boolean b = field.PrimaryKey;
            // SQLite要求自增必须是主键
            if (field.Identity && !field.PrimaryKey)
            {
                // 取消所有主键
                field.Table.Columns.ForEach(dc => dc.PrimaryKey = false);

                // 自增字段作为主键
                field.PrimaryKey = true;
            }
            //try
            {
                str = base.GetFieldConstraints(field, onlyDefine);
            }
            //finally { if (field.Identity)field.PrimaryKey = b; }

            if (field.Identity) str += " AUTOINCREMENT";

            // 给字符串字段加上忽略大小写,否则admin和Admin是查不出来的
            if (field.DataType == typeof(String)) str += " COLLATE NOCASE";

            return str;
        }
        #endregion

        #region 数据定义
        protected override void CreateDatabase()
        {
            if (!(Database as SQLite).IsMemoryDatabase) base.CreateDatabase();
        }

        protected override void DropDatabase()
        {
            if (!(Database as SQLite).IsMemoryDatabase) base.DropDatabase();
        }

        public override String CreateIndexSQL(IDataIndex index)
        {
            var sb = new StringBuilder();
            if (index.Unique)
                sb.Append("Create Unique Index ");
            else
                sb.Append("Create Index ");

            //sb.Append(FormatName(index.Name));
            // SQLite中不同表的索引名也不能相同
            sb.Append(FormatName(index.Table.Name));
            foreach (var item in index.Columns)
            {
                sb.AppendFormat("_{0}", item);
            }

            sb.AppendFormat(" On {0} (", FormatName(index.Table.Name));
            for (int i = 0; i < index.Columns.Length; i++)
            {
                if (i > 0) sb.Append(", ");
                sb.Append(FormatName(index.Columns[i]));
                //else
                //    sb.AppendFormat("{0} {1}", FormatKeyWord(index.Columns[i].Name), isAscs[i].Value ? "Asc" : "Desc");
            }
            sb.Append(")");

            return sb.ToString();
        }

        /// <summary>删除索引方法</summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public override string DropIndexSQL(IDataIndex index)
        {
            return String.Format("Drop Index {0}", FormatName(index.Name));
        }

        protected override string CheckColumnsChange(IDataTable entitytable, IDataTable dbtable, NegativeSetting setting)
        {
            foreach (var item in entitytable.Columns)
            {
                // 自增字段必须是主键
                if (item.Identity && !item.PrimaryKey)
                {
                    // 取消所有主键
                    item.Table.Columns.ForEach(dc => dc.PrimaryKey = false);

                    // 自增字段作为主键
                    item.PrimaryKey = true;
                    break;
                }
            }

            //String sql = base.CheckColumnsChange(entitytable, dbtable, onlySql);
            // 把onlySql设为true,让基类只产生语句而不执行
            var set = new NegativeSetting();
            set.CheckOnly = true;
            set.NoDelete = setting.NoDelete;
            var sql = base.CheckColumnsChange(entitytable, dbtable, set);
            if (String.IsNullOrEmpty(sql)) return sql;

            sql = ReBuildTable(entitytable, dbtable);
            if (String.IsNullOrEmpty(sql) || setting.CheckOnly) return sql;

            Boolean flag = true;
            // 如果设定不允许删
            if (setting.NoDelete)
            {
                // 看看有没有数据库里面有而实体库里没有的
                foreach (var item in dbtable.Columns)
                {
                    var dc = entitytable.GetColumn(item.Name);
                    if (dc == null)
                    {
                        flag = false;
                        break;
                    }
                }
            }
            if (flag) Database.CreateSession().Execute(sql);

            return sql;
        }

        //protected override bool IsColumnChanged(IDataColumn entityColumn, IDataColumn dbColumn, IDatabase entityDb)
        //{
        //    //// SQLite的自增将会被识别为64位,而实际应用一般使用32位,不需要修改
        //    //if (entityColumn.DataType == typeof(Int32) && entityColumn.Identity &&
        //    //    dbColumn.DataType == typeof(Int64) && dbColumn.Identity)
        //    //{
        //    //    // 克隆一个,修改类型
        //    //    entityColumn = entityColumn.Clone(entityColumn.Table);
        //    //    entityColumn.DataType = typeof(Int64);
        //    //}

        //    if (!base.IsColumnChanged(entityColumn, dbColumn, entityDb)) return false;

        //    // 自增字段必须是主键
        //    if (entityColumn.Identity && !entityColumn.PrimaryKey)
        //    {
        //        // 取消所有主键
        //        entityColumn.Table.Columns.ForEach(dc => dc.PrimaryKey = false);

        //        // 自增字段作为主键
        //        entityColumn.PrimaryKey = true;
        //    }

        //    return true;
        //}
        #endregion

        #region 表和字段备注
        public override string AddTableDescriptionSQL(IDataTable table)
        {
            // 返回Empty,告诉反向工程,该数据库类型不支持该功能,请不要输出日志
            return String.Empty;
        }

        public override string DropTableDescriptionSQL(IDataTable table)
        {
            return String.Empty;
        }

        public override string AddColumnDescriptionSQL(IDataColumn field)
        {
            return String.Empty;
        }

        public override string DropColumnDescriptionSQL(IDataColumn field)
        {
            return String.Empty;
        }
        #endregion

        #region 默认值
        public override string AddDefaultSQL(IDataColumn field)
        {
            return String.Empty;
        }

        public override string DropDefaultSQL(IDataColumn field)
        {
            return String.Empty;
        }
        #endregion
    }
}