mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 所有国产数据库支持 CustomMySql、CustomPostgreSQL、CustomOracle、CustomSqlServer 自定义适配;
This commit is contained in:
		@@ -0,0 +1,121 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlDelete<T1> : Internal.CommonProvider.DeleteProvider<T1>
 | 
			
		||||
    {
 | 
			
		||||
        public CustomMySqlDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override List<T1> ExecuteDeleted()
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            ToSqlFetch(sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.Append(sbret).ToString();
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret.AddRange(_orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms));
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (dbParms != null)
 | 
			
		||||
            {
 | 
			
		||||
                this.ClearData();
 | 
			
		||||
                sbret.Clear();
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        async public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            await ToSqlFetchAsync(async sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.Append(sbret).ToString();
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret.AddRange(await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken));
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (dbParms != null)
 | 
			
		||||
            {
 | 
			
		||||
                this.ClearData();
 | 
			
		||||
                sbret.Clear();
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,201 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using FreeSql.Internal.ObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public CustomMySqlInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal bool InternalIsIgnoreInto = false;
 | 
			
		||||
        internal IFreeSql InternalOrm => _orm;
 | 
			
		||||
        internal TableInfo InternalTable => _table;
 | 
			
		||||
        internal DbParameter[] InternalParams => _params;
 | 
			
		||||
        internal DbConnection InternalConnection => _connection;
 | 
			
		||||
        internal DbTransaction InternalTransaction => _transaction;
 | 
			
		||||
        internal CommonUtils InternalCommonUtils => _commonUtils;
 | 
			
		||||
        internal CommonExpression InternalCommonExpression => _commonExpression;
 | 
			
		||||
        internal List<T1> InternalSource => _source;
 | 
			
		||||
        internal Dictionary<string, bool> InternalIgnore => _ignore;
 | 
			
		||||
        internal void InternalClearData() => ClearData();
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
 | 
			
		||||
        public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
 | 
			
		||||
        public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public override string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            if (InternalIsIgnoreInto == false) return base.ToSqlValuesOrSelectUnionAll();
 | 
			
		||||
            var sql = base.ToSqlValuesOrSelectUnionAll();
 | 
			
		||||
            return $"INSERT IGNORE INTO {sql.Substring(12)}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override long RawExecuteIdentity()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            Object<DbConnection> poolConn = null;
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, string.Concat(sql, "; SELECT LAST_INSERT_ID();", _commandTimeout), _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var conn = _connection;
 | 
			
		||||
                if (_transaction != null) conn = _transaction.Connection;
 | 
			
		||||
                if (conn == null)
 | 
			
		||||
                {
 | 
			
		||||
                    poolConn = _orm.Ado.MasterPool.Get();
 | 
			
		||||
                    conn = poolConn.Value;
 | 
			
		||||
                }
 | 
			
		||||
                _orm.Ado.ExecuteNonQuery(conn, _transaction, CommandType.Text, sql, _commandTimeout, _params);
 | 
			
		||||
                ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(conn, _transaction, CommandType.Text, " SELECT LAST_INSERT_ID()", _commandTimeout)), out var trylng) ? trylng : 0;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                if (poolConn != null)
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(poolConn);
 | 
			
		||||
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        protected override List<T1> RawExecuteInserted()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
 | 
			
		||||
        public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
 | 
			
		||||
        public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
 | 
			
		||||
 | 
			
		||||
        async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            Object<DbConnection> poolConn = null;
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, string.Concat(sql, "; SELECT LAST_INSERT_ID();"), _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var conn = _connection;
 | 
			
		||||
                if (_transaction != null) conn = _transaction.Connection;
 | 
			
		||||
                if (conn == null)
 | 
			
		||||
                {
 | 
			
		||||
                    poolConn = _orm.Ado.MasterPool.Get();
 | 
			
		||||
                    conn = poolConn.Value;
 | 
			
		||||
                }
 | 
			
		||||
                await _orm.Ado.ExecuteNonQueryAsync(conn, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
 | 
			
		||||
                ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(conn, _transaction, CommandType.Text, " SELECT LAST_INSERT_ID()", _commandTimeout, null, cancellationToken)), out var trylng) ? trylng : 0;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                if (poolConn != null)
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(poolConn);
 | 
			
		||||
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async protected override Task<List<T1>> RawExecuteInsertedAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,88 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlInsertOrUpdate<T1> : Internal.CommonProvider.InsertOrUpdateProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public CustomMySqlInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            var dbParams = new List<DbParameter>();
 | 
			
		||||
            if (_sourceSql != null)
 | 
			
		||||
            {
 | 
			
		||||
                var data = new List<T1>();
 | 
			
		||||
                data.Add((T1)_table.Type.CreateInstanceGetDefaultValue());
 | 
			
		||||
                var sql = getInsertSql(data, false, false);
 | 
			
		||||
                var sb = new StringBuilder();
 | 
			
		||||
                sb.Append(sql.Substring(0, sql.IndexOf(") VALUES")));
 | 
			
		||||
                sb.Append(") \r\n");
 | 
			
		||||
                WriteSourceSelectUnionAll(null, sb, null);
 | 
			
		||||
                if (_doNothing == false)
 | 
			
		||||
                    sb.Append(sql.Substring(sql.IndexOf("\r\nON DUPLICATE KEY UPDATE\r\n") + 2));
 | 
			
		||||
                else
 | 
			
		||||
                    throw new Exception("Not implemented! fsql.InsertOrUpdate + SetSource(sql) + IfExistsDoNothing + MySql");
 | 
			
		||||
                return sb.ToString();
 | 
			
		||||
            }
 | 
			
		||||
            if (_source?.Any() != true) return null;
 | 
			
		||||
 | 
			
		||||
            var sqls = new string[2];
 | 
			
		||||
            var ds = SplitSourceByIdentityValueIsNull(_source);
 | 
			
		||||
            if (ds.Item1.Any()) sqls[0] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item1.Select(a => getInsertSql(a, false, true)));
 | 
			
		||||
            if (ds.Item2.Any()) sqls[1] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item2.Select(a => getInsertSql(a, true, true)));
 | 
			
		||||
            _params = dbParams.ToArray();
 | 
			
		||||
            if (ds.Item2.Any() == false) return sqls[0];
 | 
			
		||||
            if (ds.Item1.Any() == false) return sqls[1];
 | 
			
		||||
            return string.Join("\r\n\r\n;\r\n\r\n", sqls);
 | 
			
		||||
 | 
			
		||||
            string getInsertSql(List<T1> data, bool flagInsert, bool noneParameter)
 | 
			
		||||
            {
 | 
			
		||||
                var insert = _orm.Insert<T1>()
 | 
			
		||||
                    .AsTable(_tableRule).AsType(_table.Type)
 | 
			
		||||
                    .WithConnection(_connection)
 | 
			
		||||
                    .WithTransaction(_transaction)
 | 
			
		||||
                    .NoneParameter(noneParameter) as Internal.CommonProvider.InsertProvider<T1>;
 | 
			
		||||
                insert._source = data;
 | 
			
		||||
                insert._table = _table;
 | 
			
		||||
                insert._noneParameterFlag = flagInsert ? "cuc" : "cu";
 | 
			
		||||
 | 
			
		||||
                string sql = "";
 | 
			
		||||
                if (IdentityColumn != null && flagInsert) sql = insert.ToSql();
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    insert.InsertIdentity();
 | 
			
		||||
                    if (_doNothing == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var cols = _table.Columns.Values.Where(a => _tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
 | 
			
		||||
                        sql = new CustomMySqlOnDuplicateKeyUpdate<T1>(insert)
 | 
			
		||||
                            .UpdateColumns(cols.Select(a => a.Attribute.Name).ToArray())
 | 
			
		||||
                            .ToSql();
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_tempPrimarys.Any() == false) throw new Exception(CoreStrings.Entity_Must_Primary_Key("fsql.InsertOrUpdate + IfExistsDoNothing + MySql ", _table.CsName));
 | 
			
		||||
                        sql = insert.ToSqlValuesOrSelectUnionAllExtension101(false, (rowd, idx, sb) =>
 | 
			
		||||
                            sb.Append(" \r\n FROM dual WHERE NOT EXISTS(").Append(
 | 
			
		||||
                                _orm.Select<T1>()
 | 
			
		||||
                                .AsTable((_, __) => _tableRule?.Invoke(__)).AsType(_table.Type)
 | 
			
		||||
                                .DisableGlobalFilter()
 | 
			
		||||
                                .WhereDynamic(rowd)
 | 
			
		||||
                                .Limit(1).ToSql("1").Replace(" \r\n", " \r\n    ")).Append(")"));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(sql)) return null;
 | 
			
		||||
                if (insert._params?.Any() == true) dbParams.AddRange(insert._params);
 | 
			
		||||
                return sql;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,168 @@
 | 
			
		||||
using FreeSql.Aop;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
    public class CustomMySqlOnDuplicateKeyUpdate<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        internal CustomMySqlInsert<T1> _mysqlInsert;
 | 
			
		||||
        internal CustomMySqlUpdate<T1> _mysqlUpdatePriv;
 | 
			
		||||
        internal CustomMySqlUpdate<T1> _mysqlUpdate => _mysqlUpdatePriv ?? (_mysqlUpdatePriv = new CustomMySqlUpdate<T1>(_mysqlInsert.InternalOrm, _mysqlInsert.InternalCommonUtils, _mysqlInsert.InternalCommonExpression, null).NoneParameter().SetSource(_mysqlInsert.InternalSource) as CustomMySqlUpdate<T1>);
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate(IInsert<T1> insert)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlInsert = insert as CustomMySqlInsert<T1>;
 | 
			
		||||
            if (_mysqlInsert == null) throw new Exception(CoreStrings.S_Features_Unique("OnDuplicateKeyUpdate", "Odbc/MySql"));
 | 
			
		||||
            if (_mysqlInsert._noneParameterFlag == "c") _mysqlInsert._noneParameterFlag = "cu";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected void ClearData()
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlInsert.InternalClearData();
 | 
			
		||||
            _mysqlUpdatePriv = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.IgnoreColumns(columns);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> UpdateColumns(Expression<Func<T1, object>> columns)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.UpdateColumns(columns);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> IgnoreColumns(string[] columns)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.IgnoreColumns(columns);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> UpdateColumns(string[] columns)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.UpdateColumns(columns);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> column, TMember value)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.Set(column, value);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> exp)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.Set(exp);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        public CustomMySqlOnDuplicateKeyUpdate<T1> SetRaw(string sql)
 | 
			
		||||
        {
 | 
			
		||||
            _mysqlUpdate.SetRaw(sql);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(_mysqlInsert.ToSql()).Append("\r\nON DUPLICATE KEY UPDATE\r\n");
 | 
			
		||||
 | 
			
		||||
            var sbSetEmpty = _mysqlUpdate.InternalSbSet.Length == 0;
 | 
			
		||||
            var sbSetIncrEmpty = _mysqlUpdate.InternalSbSetIncr.Length == 0;
 | 
			
		||||
            if (sbSetEmpty == false || sbSetIncrEmpty == false)
 | 
			
		||||
            {
 | 
			
		||||
                if (sbSetEmpty == false) sb.Append(_mysqlUpdate.InternalSbSet.ToString().Substring(2));
 | 
			
		||||
                if (sbSetIncrEmpty == false) sb.Append(sbSetEmpty ? _mysqlUpdate.InternalSbSetIncr.ToString().Substring(2) : _mysqlUpdate.InternalSbSetIncr.ToString());
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var colidx = 0;
 | 
			
		||||
                foreach (var col in _mysqlInsert.InternalTable.Columns.Values)
 | 
			
		||||
                {
 | 
			
		||||
                    if (col.Attribute.IsPrimary || _mysqlUpdate.InternalIgnore.ContainsKey(col.Attribute.Name)) continue;
 | 
			
		||||
 | 
			
		||||
                    if (colidx > 0) sb.Append(", \r\n");
 | 
			
		||||
 | 
			
		||||
                    if (col.Attribute.IsVersion == true && col.Attribute.MapType != typeof(byte[]))
 | 
			
		||||
                    {
 | 
			
		||||
                        var field = _mysqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name);
 | 
			
		||||
                        sb.Append(field).Append(" = ").Append(field).Append(" + 1");
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (_mysqlInsert.InternalIgnore.ContainsKey(col.Attribute.Name))
 | 
			
		||||
                    {
 | 
			
		||||
                        var caseWhen = _mysqlUpdate.InternalWhereCaseSource(col.CsName, sqlval => sqlval).Trim();
 | 
			
		||||
                        sb.Append(caseWhen);
 | 
			
		||||
                        if (caseWhen.EndsWith(" END")) _mysqlUpdate.InternalToSqlCaseWhenEnd(sb, col);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        var field = _mysqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name);
 | 
			
		||||
                        sb.Append(field).Append(" = VALUES(").Append(field).Append(")");
 | 
			
		||||
                    }
 | 
			
		||||
                    ++colidx;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public long ExecuteAffrows()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            var before = new CurdBeforeEventArgs(_mysqlInsert.InternalTable.Type, _mysqlInsert.InternalTable, CurdType.Insert, sql, _mysqlInsert.InternalParams);
 | 
			
		||||
            _mysqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_mysqlInsert, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = _mysqlInsert.InternalOrm.Ado.ExecuteNonQuery(_mysqlInsert.InternalConnection, _mysqlInsert.InternalTransaction, CommandType.Text, sql, _mysqlInsert._commandTimeout, _mysqlInsert.InternalParams);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _mysqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_mysqlInsert, after);
 | 
			
		||||
                ClearData();
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        async public Task<long> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            var before = new CurdBeforeEventArgs(_mysqlInsert.InternalTable.Type, _mysqlInsert.InternalTable, CurdType.Insert, sql, _mysqlInsert.InternalParams);
 | 
			
		||||
            _mysqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_mysqlInsert, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = await _mysqlInsert.InternalOrm.Ado.ExecuteNonQueryAsync(_mysqlInsert.InternalConnection, _mysqlInsert.InternalTransaction, CommandType.Text, sql, _mysqlInsert._commandTimeout, _mysqlInsert.InternalParams, cancellationToken);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _mysqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_mysqlInsert, after);
 | 
			
		||||
                ClearData();
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,212 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent, List<GlobalFilter.Item> _whereGlobalFilter, IFreeSql _orm)
 | 
			
		||||
        {
 | 
			
		||||
            if (_orm.CodeFirst.IsAutoSyncStructure)
 | 
			
		||||
                _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
 | 
			
		||||
 | 
			
		||||
            if (_whereGlobalFilter.Any())
 | 
			
		||||
                foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
 | 
			
		||||
                    tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true);
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var tbUnionsGt0 = tbUnions.Count > 1;
 | 
			
		||||
            for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                if (tbUnionsIdx > 0) sb.Append("\r\n \r\nUNION ALL\r\n \r\n");
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(_select).Append(" * from (");
 | 
			
		||||
                var tbUnion = tbUnions[tbUnionsIdx];
 | 
			
		||||
 | 
			
		||||
                var sbnav = new StringBuilder();
 | 
			
		||||
                sb.Append(_select);
 | 
			
		||||
                if (_distinct) sb.Append("DISTINCT ");
 | 
			
		||||
                sb.Append(field).Append(" \r\nFROM ");
 | 
			
		||||
                var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
 | 
			
		||||
                var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
 | 
			
		||||
                for (var a = 0; a < tbsfrom.Length; a++)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias);
 | 
			
		||||
                    if (tbsjoin.Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
 | 
			
		||||
                        for (var b = 1; b < tbsfrom.Length; b++)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias);
 | 
			
		||||
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
 | 
			
		||||
                                sb.Append(" ON ").Append(onSql);
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                    else sb.Append(" AND ").Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
 | 
			
		||||
                        if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
 | 
			
		||||
                        if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND ").Append(tbsfrom[a].Cascade);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (a < tbsfrom.Length - 1) sb.Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var tb in tbsjoin)
 | 
			
		||||
                {
 | 
			
		||||
                    switch (tb.Type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case SelectTableInfoType.Parent:
 | 
			
		||||
                        case SelectTableInfoType.RawJoin:
 | 
			
		||||
                            continue;
 | 
			
		||||
                        case SelectTableInfoType.LeftJoin:
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.InnerJoin:
 | 
			
		||||
                            sb.Append(" \r\nINNER JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.RightJoin:
 | 
			
		||||
                            sb.Append(" \r\nRIGHT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (_join.Length > 0) sb.Append(_join);
 | 
			
		||||
 | 
			
		||||
                sbnav.Append(_where);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_tables[0].Cascade))
 | 
			
		||||
                    sbnav.Append(" AND ").Append(_tables[0].Cascade);
 | 
			
		||||
 | 
			
		||||
                if (sbnav.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(_groupby) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_groupby);
 | 
			
		||||
                    if (string.IsNullOrEmpty(_having) == false)
 | 
			
		||||
                        sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append(_orderby);
 | 
			
		||||
                if (_skip > 0 || _limit > 0)
 | 
			
		||||
                    sb.Append(" \r\nlimit ").Append(Math.Max(0, _skip)).Append(",").Append(_limit > 0 ? _limit : -1);
 | 
			
		||||
 | 
			
		||||
                sbnav.Clear();
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(") ftb");
 | 
			
		||||
            }
 | 
			
		||||
            return sb.Append(_tosqlAppendContent).ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm, _commonUtils, _commonExpression, null); CustomMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : FreeSql.Internal.CommonProvider.Select11Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : FreeSql.Internal.CommonProvider.Select12Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : FreeSql.Internal.CommonProvider.Select13Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : FreeSql.Internal.CommonProvider.Select14Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> : FreeSql.Internal.CommonProvider.Select15Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : FreeSql.Internal.CommonProvider.Select16Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,170 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal StringBuilder InternalSbSet => _set;
 | 
			
		||||
        internal StringBuilder InternalSbSetIncr => _setIncr;
 | 
			
		||||
        internal Dictionary<string, bool> InternalIgnore => _ignore;
 | 
			
		||||
        internal void InternalResetSource(List<T1> source) => _source = source;
 | 
			
		||||
        internal string InternalWhereCaseSource(string CsName, Func<string, string> thenValue) => WhereCaseSource(CsName, thenValue);
 | 
			
		||||
        internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col);
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
 | 
			
		||||
        public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteUpdated()
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            ToSqlFetch(sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.Concat(_paramsSource).ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.Append(sbret).ToString();
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var rettmp = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms);
 | 
			
		||||
                    ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
 | 
			
		||||
                    ret.AddRange(rettmp);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            sbret?.Clear();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
 | 
			
		||||
        {
 | 
			
		||||
            if (primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                var pk = primarys.First();
 | 
			
		||||
                caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append("CONCAT(");
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) caseWhen.Append(", '+', ");
 | 
			
		||||
                caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append(")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
 | 
			
		||||
        {
 | 
			
		||||
            if (primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            sb.Append("CONCAT(");
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) sb.Append(", '+', ");
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", pk.GetDbValue(d)));
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
            sb.Append(")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
 | 
			
		||||
        public override Task<List<T1>> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
 | 
			
		||||
 | 
			
		||||
        async protected override Task<List<T1>> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            DbParameter[] dbParms = null;
 | 
			
		||||
            StringBuilder sbret = null;
 | 
			
		||||
            await ToSqlFetchAsync(async sb =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dbParms == null)
 | 
			
		||||
                {
 | 
			
		||||
                    dbParms = _params.Concat(_paramsSource).ToArray();
 | 
			
		||||
                    sbret = new StringBuilder();
 | 
			
		||||
                    sbret.Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
                    var colidx = 0;
 | 
			
		||||
                    foreach (var col in _table.Columns.Values)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (colidx > 0) sbret.Append(", ");
 | 
			
		||||
                        sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                        ++colidx;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var sql = sb.Append(sbret).ToString();
 | 
			
		||||
                var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
 | 
			
		||||
                Exception exception = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var rettmp = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken);
 | 
			
		||||
                    ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
 | 
			
		||||
                    ret.AddRange(rettmp);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfterHandler?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            sbret?.Clear();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,68 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using FreeSql.Internal.ObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
    public class CustomMySqlAdo : FreeSql.Internal.CommonProvider.AdoProvider
 | 
			
		||||
    {
 | 
			
		||||
        DbProviderFactory Factory => FreeSqlCustomAdapterGlobalExtensions.GetDbProviderFactory(_util._orm);
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlAdo() : base(DataType.OdbcMySql, null, null) { }
 | 
			
		||||
        public CustomMySqlAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.OdbcMySql, masterConnectionString, slaveConnectionStrings)
 | 
			
		||||
        {
 | 
			
		||||
            base._util = util;
 | 
			
		||||
            if (connectionFactory != null)
 | 
			
		||||
            {
 | 
			
		||||
                var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.SqlServer, connectionFactory);
 | 
			
		||||
                MasterPool = pool;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            throw new Exception(CoreStrings.S_CustomAdapter_OnlySuppport_UseConnectionFactory);
 | 
			
		||||
        }
 | 
			
		||||
        public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn)
 | 
			
		||||
        {
 | 
			
		||||
            if (param == null) return "NULL";
 | 
			
		||||
            if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false))
 | 
			
		||||
                param = Utils.GetDataReaderValue(mapType, param);
 | 
			
		||||
 | 
			
		||||
            if (param is bool || param is bool?)
 | 
			
		||||
                return (bool)param ? 1 : 0;
 | 
			
		||||
            else if (param is string)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\"), "'"); //只有 mysql 需要处理反斜杠
 | 
			
		||||
            else if (param is char)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace('\0', ' '), "'");
 | 
			
		||||
            else if (param is Enum)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace(", ", ","), "'"); //((Enum)val).ToInt64();
 | 
			
		||||
            else if (decimal.TryParse(string.Concat(param), out var trydec))
 | 
			
		||||
                return param;
 | 
			
		||||
            else if (param is DateTime || param is DateTime?)
 | 
			
		||||
                return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.fff"), "'");
 | 
			
		||||
            else if (param is TimeSpan || param is TimeSpan?)
 | 
			
		||||
                return ((TimeSpan)param).Ticks / 10;
 | 
			
		||||
            else if (param is byte[])
 | 
			
		||||
                return $"0x{CommonUtils.BytesSqlRaw(param as byte[])}";
 | 
			
		||||
            else if (param is IEnumerable) 
 | 
			
		||||
                return AddslashesIEnumerable(param, mapType, mapColumn);
 | 
			
		||||
 | 
			
		||||
            return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\"), "'");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbCommand CreateCommand()
 | 
			
		||||
        {
 | 
			
		||||
            return Factory.CreateCommand();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            pool.Return(conn);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										408
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlCodeFirst.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										408
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlCodeFirst.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,408 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using FreeSql.Internal.ObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlCodeFirst : Internal.CommonProvider.CodeFirstProvider
 | 
			
		||||
    {
 | 
			
		||||
        public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
 | 
			
		||||
        public CustomMySqlCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
 | 
			
		||||
 | 
			
		||||
        static object _dicCsToDbLock = new object();
 | 
			
		||||
        static Dictionary<string, CsToDb<DbType>> _dicCsToDb = new Dictionary<string, CsToDb<DbType>>() {
 | 
			
		||||
                { typeof(bool).FullName, CsToDb.New(DbType.Boolean, "bit","bit(1) NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DbType.Boolean, "bit","bit(1)", null, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "tinyint", "tinyint(3) NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "tinyint", "tinyint(3)", false, true, null) },
 | 
			
		||||
                { typeof(short).FullName, CsToDb.New(DbType.Int16, "smallint","smallint(6) NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, "smallint", "smallint(6)", false, true, null) },
 | 
			
		||||
                { typeof(int).FullName, CsToDb.New(DbType.Int32, "int", "int(11) NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, "int", "int(11)", false, true, null) },
 | 
			
		||||
                { typeof(long).FullName, CsToDb.New(DbType.Int64, "bigint","bigint(20) NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, "bigint","bigint(20)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte).FullName, CsToDb.New(DbType.Byte, "tinyint","tinyint(3) unsigned NOT NULL", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, "tinyint","tinyint(3) unsigned", true, true, null) },
 | 
			
		||||
                { typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "smallint","smallint(5) unsigned NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "smallint", "smallint(5) unsigned", true, true, null) },
 | 
			
		||||
                { typeof(uint).FullName, CsToDb.New(DbType.UInt32, "int", "int(10) unsigned NOT NULL", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "int", "int(10) unsigned", true, true, null) },
 | 
			
		||||
                { typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "bigint", "bigint(20) unsigned NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "bigint", "bigint(20) unsigned", true, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(double).FullName, CsToDb.New(DbType.Double, "double", "double NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, "double", "double", false, true, null) },
 | 
			
		||||
                { typeof(float).FullName, CsToDb.New(DbType.Single, "float","float NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, "float","float", false, true, null) },
 | 
			
		||||
                { typeof(decimal).FullName, CsToDb.New(DbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(TimeSpan).FullName, CsToDb.New(DbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(DbType.Time, "time", "time",false, true, null) },
 | 
			
		||||
                { typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, "datetime(3)", "datetime(3) NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, "datetime(3)", "datetime(3)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte[]).FullName, CsToDb.New(DbType.Binary, "varbinary", "varbinary(255)", false, null, new byte[0]) },
 | 
			
		||||
                { typeof(string).FullName, CsToDb.New(DbType.String, "varchar", "varchar(255)", false, null, "") },
 | 
			
		||||
                { typeof(char).FullName, CsToDb.New(DbType.AnsiString, "char", "char(1) NULL", false, null, '\0') },
 | 
			
		||||
 | 
			
		||||
                { typeof(Guid).FullName, CsToDb.New(DbType.Guid, "char", "char(36) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(DbType.Guid, "char", "char(36)", false, true, null) },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public override DbInfoResult GetDbInfo(Type type)
 | 
			
		||||
        {
 | 
			
		||||
            if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue);
 | 
			
		||||
            if (type.IsArray) return null;
 | 
			
		||||
            var enumType = type.IsEnum ? type : null;
 | 
			
		||||
            if (enumType == null && type.IsNullableType())
 | 
			
		||||
            {
 | 
			
		||||
                var genericTypes = type.GetGenericArguments();
 | 
			
		||||
                if (genericTypes.Length == 1 && genericTypes.First().IsEnum) enumType = genericTypes.First();
 | 
			
		||||
            }
 | 
			
		||||
            if (enumType != null)
 | 
			
		||||
            {
 | 
			
		||||
                var names = string.Join(",", Enum.GetNames(enumType).Select(a => _commonUtils.FormatSql("{0}", a)));
 | 
			
		||||
                var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
 | 
			
		||||
                    CsToDb.New(DbType.String, "set", $"set({names}){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()) :
 | 
			
		||||
                    CsToDb.New(DbType.String, "enum", $"enum({names}){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue());
 | 
			
		||||
                if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    lock (_dicCsToDbLock)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                            _dicCsToDb.Add(type.FullName, newItem);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return new DbInfoResult((int)newItem.type, newItem.dbtype, newItem.dbtypeFull, newItem.isnullable, newItem.defaultValue);
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override string GetComparisonDDLStatements(params TypeAndName[] objects)
 | 
			
		||||
        {
 | 
			
		||||
            Object<DbConnection> conn = null;
 | 
			
		||||
            string database = null;
 | 
			
		||||
            
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
 | 
			
		||||
                database = conn.Value.Database;
 | 
			
		||||
 | 
			
		||||
                var sb = new StringBuilder();
 | 
			
		||||
                foreach (var obj in objects)
 | 
			
		||||
                {
 | 
			
		||||
                    if (sb.Length > 0) sb.Append("\r\n");
 | 
			
		||||
                    var tb = _commonUtils.GetTableByEntity(obj.entityType);
 | 
			
		||||
                    if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName));
 | 
			
		||||
                    if (tb.Columns.Any() == false) throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName));
 | 
			
		||||
                    var tbname = _commonUtils.SplitTableName(tb.DbName);
 | 
			
		||||
                    if (tbname?.Length == 1) tbname = new[] { database, tbname[0] };
 | 
			
		||||
 | 
			
		||||
                    var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名
 | 
			
		||||
                    if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] };
 | 
			
		||||
                    if (string.IsNullOrEmpty(obj.tableName) == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var tbtmpname = _commonUtils.SplitTableName(obj.tableName);
 | 
			
		||||
                        if (tbtmpname?.Length == 1) tbtmpname = new[] { database, tbtmpname[0] };
 | 
			
		||||
                        if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1])
 | 
			
		||||
                        {
 | 
			
		||||
                            tbname = tbtmpname;
 | 
			
		||||
                            tboldname = null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (string.Compare(tbname[0], database, true) != 0 && LocalExecuteScalar(database, _commonUtils.FormatSql(" select 1 from information_schema.schemata where schema_name={0}", tbname[0])) == null) //创建数据库
 | 
			
		||||
                        sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(" default charset utf8 COLLATE utf8_general_ci;\r\n");
 | 
			
		||||
 | 
			
		||||
                    var sbalter = new StringBuilder();
 | 
			
		||||
                    var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
 | 
			
		||||
                    if (LocalExecuteScalar(tbname[0], _commonUtils.FormatSql(" SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}", tbname)) == null)
 | 
			
		||||
                    { //表不存在
 | 
			
		||||
                        if (tboldname != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (string.Compare(tboldname[0], tbname[0], true) != 0 && LocalExecuteScalar(database, _commonUtils.FormatSql(" select 1 from information_schema.schemata where schema_name={0}", tboldname[0])) == null ||
 | 
			
		||||
                                LocalExecuteScalar(tboldname[0], _commonUtils.FormatSql(" SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}", tboldname)) == null)
 | 
			
		||||
                                //数据库或表不存在
 | 
			
		||||
                                tboldname = null;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tboldname == null)
 | 
			
		||||
                        {
 | 
			
		||||
                            //创建表
 | 
			
		||||
                            var createTableName = _commonUtils.QuoteSqlName(tbname[0], tbname[1]);
 | 
			
		||||
                            sb.Append("CREATE TABLE IF NOT EXISTS ").Append(createTableName).Append(" ( ");
 | 
			
		||||
                            foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                                if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbcol.Comment) == false) sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment));
 | 
			
		||||
                                sb.Append(",");
 | 
			
		||||
                            }
 | 
			
		||||
                            if (tb.Primarys.Any())
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append(" \r\n  PRIMARY KEY (");
 | 
			
		||||
                                foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                                sb.Remove(sb.Length - 2, 2).Append("),");
 | 
			
		||||
                            }
 | 
			
		||||
                            //创建表的索引,感谢 @mafeng8,这样写可以支持自增不是主键的情况
 | 
			
		||||
                            foreach (var uk in tb.Indexes)
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append(" \r\n  ");
 | 
			
		||||
                                if (uk.IsUnique) sb.Append("UNIQUE ");
 | 
			
		||||
                                sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append("(");
 | 
			
		||||
                                foreach (var tbcol in uk.Columns)
 | 
			
		||||
                                {
 | 
			
		||||
                                    sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
 | 
			
		||||
                                    if (tbcol.IsDesc) sb.Append(" DESC");
 | 
			
		||||
                                    sb.Append(", ");
 | 
			
		||||
                                }
 | 
			
		||||
                                sb.Remove(sb.Length - 2, 2).Append("),");
 | 
			
		||||
                            }
 | 
			
		||||
                            sb.Remove(sb.Length - 1, 1);
 | 
			
		||||
                            sb.Append("\r\n) Engine=InnoDB");
 | 
			
		||||
                            if (string.IsNullOrEmpty(tb.Comment) == false)
 | 
			
		||||
                                sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
 | 
			
		||||
                            sb.Append(";\r\n");
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        //如果新表,旧表在一个数据库下,直接修改表名
 | 
			
		||||
                        if (string.Compare(tbname[0], tboldname[0], true) == 0)
 | 
			
		||||
                            sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1])).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //如果新表,旧表不在一起,创建新表,导入数据,删除旧表
 | 
			
		||||
                            istmpatler = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                        tboldname = null; //如果新表已经存在,不走改表名逻辑
 | 
			
		||||
 | 
			
		||||
                    //对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
 | 
			
		||||
                    var sql = _commonUtils.FormatSql(@"
 | 
			
		||||
select
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.column_type,
 | 
			
		||||
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
 | 
			
		||||
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
 | 
			
		||||
a.column_comment 'comment'
 | 
			
		||||
from information_schema.columns a
 | 
			
		||||
where a.table_schema in ({0}) and a.table_name in ({1})", tboldname ?? tbname);
 | 
			
		||||
                    var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                    var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a =>
 | 
			
		||||
                    {
 | 
			
		||||
                        var a1 = string.Concat(a[1]);
 | 
			
		||||
                        if (a1 == "datetime") a1 = string.Concat(a1, "(0)");
 | 
			
		||||
                        return new
 | 
			
		||||
                        {
 | 
			
		||||
                            column = string.Concat(a[0]),
 | 
			
		||||
                            sqlType = a1,
 | 
			
		||||
                            is_nullable = string.Concat(a[2]) == "1",
 | 
			
		||||
                            is_identity = string.Concat(a[3]) == "1",
 | 
			
		||||
                            is_unsigned = string.Concat(a[1]).EndsWith(" unsigned"),
 | 
			
		||||
                            comment = string.Concat(a[4])
 | 
			
		||||
                        };
 | 
			
		||||
                    }, StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
 | 
			
		||||
                    if (istmpatler == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var existsPrimary = LocalExecuteScalar(tbname[0], _commonUtils.FormatSql(" select 1 from information_schema.key_column_usage where table_schema={0} and table_name={1} and constraint_name = 'PRIMARY' limit 1", tbname));
 | 
			
		||||
                        foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        {
 | 
			
		||||
                            var isIdentityChanged = tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1;
 | 
			
		||||
                            if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
 | 
			
		||||
                                string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
 | 
			
		||||
                            {
 | 
			
		||||
                                var isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? "");
 | 
			
		||||
                                var isDbTypeChanged = tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false;
 | 
			
		||||
                                if (tbstructcol.sqlType == "datetime(0)" && Regex.IsMatch(tbcol.Attribute.DbType, @"datetime\s*\(", RegexOptions.IgnoreCase) == false)
 | 
			
		||||
                                    isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("datetime", StringComparison.CurrentCultureIgnoreCase) == false;
 | 
			
		||||
                                else if (tbstructcol.sqlType.StartsWith("datetime", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("datetime", StringComparison.CurrentCultureIgnoreCase) == false ||
 | 
			
		||||
                                        (int.TryParse(Regex.Match(tbcol.Attribute.DbType, @"datetime\s*\((\d*)", RegexOptions.IgnoreCase).Groups[1].Value, out var trydtrd) ? trydtrd : 3) !=
 | 
			
		||||
                                        (int.TryParse(Regex.Match(tbstructcol.sqlType, @"datetime\s*\((\d*)", RegexOptions.IgnoreCase).Groups[1].Value, out var trydtrd2) ? trydtrd2 : 3);
 | 
			
		||||
                                else if (tbstructcol.sqlType.StartsWith("int", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("int", StringComparison.CurrentCultureIgnoreCase) == false;
 | 
			
		||||
                                else if (tbstructcol.sqlType.StartsWith("tinyint", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("tinyint", StringComparison.CurrentCultureIgnoreCase) == false;
 | 
			
		||||
                                else if (tbstructcol.sqlType.StartsWith("smallint", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("smallint", StringComparison.CurrentCultureIgnoreCase) == false;
 | 
			
		||||
                                else if (tbstructcol.sqlType.StartsWith("bigint", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("bigint", StringComparison.CurrentCultureIgnoreCase) == false;
 | 
			
		||||
 | 
			
		||||
                                if ((tbcol.Attribute.DbType.IndexOf(" unsigned", StringComparison.CurrentCultureIgnoreCase) != -1) != tbstructcol.is_unsigned ||
 | 
			
		||||
                                isDbTypeChanged ||
 | 
			
		||||
                                tbcol.Attribute.IsNullable != tbstructcol.is_nullable ||
 | 
			
		||||
                                tbcol.Attribute.IsIdentity != tbstructcol.is_identity ||
 | 
			
		||||
                                isCommentChanged)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable && tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false)
 | 
			
		||||
                                        sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" = ").Append(tbcol.DbDefaultValue).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" IS NULL;\r\n");
 | 
			
		||||
                                    sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                                    if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
 | 
			
		||||
                                    if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" AUTO_INCREMENT");
 | 
			
		||||
                                    if (tbcol.Attribute.IsIdentity == true) sbalter.Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
 | 
			
		||||
                                    sbalter.Append(";\r\n");
 | 
			
		||||
                                }
 | 
			
		||||
                                if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0)
 | 
			
		||||
                                {
 | 
			
		||||
                                    //修改列名
 | 
			
		||||
                                    sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" CHANGE COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                                    if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
 | 
			
		||||
                                    if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" AUTO_INCREMENT");
 | 
			
		||||
                                    if (tbcol.Attribute.IsIdentity == true) sbalter.Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
 | 
			
		||||
                                    sbalter.Append(";\r\n");
 | 
			
		||||
                                }
 | 
			
		||||
                                continue;
 | 
			
		||||
                            }
 | 
			
		||||
                            //添加列
 | 
			
		||||
                            sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" ADD ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                            if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false) sbalter.Append(" DEFAULT ").Append(tbcol.DbDefaultValue);
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
 | 
			
		||||
                            if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" AUTO_INCREMENT");
 | 
			
		||||
                            if (tbcol.Attribute.IsIdentity == true) sbalter.Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
 | 
			
		||||
                            sbalter.Append(";\r\n");
 | 
			
		||||
                        }
 | 
			
		||||
                        var dsuksql = _commonUtils.FormatSql(@"
 | 
			
		||||
select 
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.index_name 'index_id',
 | 
			
		||||
0 'IsDesc',
 | 
			
		||||
case when a.non_unique = 0 then 1 else 0 end 'IsUnique'
 | 
			
		||||
from information_schema.statistics a
 | 
			
		||||
where a.table_schema IN ({0}) and a.table_name IN ({1}) and a.index_name <> 'PRIMARY'", tboldname ?? tbname);
 | 
			
		||||
                        var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]), string.Concat(a[2]), string.Concat(a[3]) });
 | 
			
		||||
                        foreach (var uk in tb.Indexes)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) continue;
 | 
			
		||||
                            var ukname = ReplaceIndexName(uk.Name, tbname[1]);
 | 
			
		||||
                            var dsukfind1 = dsuk.Where(a => string.Compare(a[1], ukname, true) == 0).ToArray();
 | 
			
		||||
                            if (dsukfind1.Any() == false || dsukfind1.Length != uk.Columns.Length || dsukfind1.Where(a => (a[3] == "1") == uk.IsUnique && uk.Columns.Where(b => string.Compare(b.Column.Attribute.Name, a[0], true) == 0 && (a[2] == "1") == b.IsDesc).Any()).Count() != uk.Columns.Length)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (dsukfind1.Any()) sbalter.Append("DROP INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
 | 
			
		||||
                                sbalter.Append("CREATE ");
 | 
			
		||||
                                if (uk.IsUnique) sbalter.Append("UNIQUE ");
 | 
			
		||||
                                sbalter.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append("(");
 | 
			
		||||
                                foreach (var tbcol in uk.Columns)
 | 
			
		||||
                                {
 | 
			
		||||
                                    sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
 | 
			
		||||
                                    if (tbcol.IsDesc) sbalter.Append(" DESC");
 | 
			
		||||
                                    sbalter.Append(", ");
 | 
			
		||||
                                }
 | 
			
		||||
                                sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (istmpatler == false)
 | 
			
		||||
                    {
 | 
			
		||||
                        var dbcomment = string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@" select table_comment from information_schema.tables where table_schema = {0} and table_name = {1}", tbname[0], tbname[1])));
 | 
			
		||||
                        if (dbcomment != (tb.Comment ?? ""))
 | 
			
		||||
                            sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" COMMENT ").Append(" ").Append(_commonUtils.FormatSql("{0}", tb.Comment ?? "")).Append(";\r\n");
 | 
			
		||||
 | 
			
		||||
                        sb.Insert(0, sbalter);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
 | 
			
		||||
                    var tablename = tboldname == null ? _commonUtils.QuoteSqlName(tbname[0], tbname[1]) : _commonUtils.QuoteSqlName(tboldname[0], tboldname[1]);
 | 
			
		||||
                    var tmptablename = _commonUtils.QuoteSqlName(tbname[0], $"FreeSqlTmp_{tbname[1]}");
 | 
			
		||||
                    //创建临时表
 | 
			
		||||
                    sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( ");
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
 | 
			
		||||
                        if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
 | 
			
		||||
                        if (string.IsNullOrEmpty(tbcol.Comment) == false) sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment));
 | 
			
		||||
                        sb.Append(",");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (tb.Primarys.Any())
 | 
			
		||||
                    {
 | 
			
		||||
                        sb.Append(" \r\n  PRIMARY KEY (");
 | 
			
		||||
                        foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                        sb.Remove(sb.Length - 2, 2).Append("),");
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Remove(sb.Length - 1, 1);
 | 
			
		||||
                    sb.Append("\r\n) Engine=InnoDB");
 | 
			
		||||
                    if (string.IsNullOrEmpty(tb.Comment) == false)
 | 
			
		||||
                        sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
 | 
			
		||||
                    sb.Append(";\r\n");
 | 
			
		||||
 | 
			
		||||
                    sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                    sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        var insertvalue = "NULL";
 | 
			
		||||
                        if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
 | 
			
		||||
                            string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
 | 
			
		||||
                        {
 | 
			
		||||
                            insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
 | 
			
		||||
                            if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
 | 
			
		||||
                            {
 | 
			
		||||
                                //insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
 | 
			
		||||
                            }
 | 
			
		||||
                            if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
 | 
			
		||||
                                insertvalue = $"ifnull({insertvalue},{tbcol.DbDefaultValue})";
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (tbcol.Attribute.IsNullable == false)
 | 
			
		||||
                            if (tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false)
 | 
			
		||||
                                insertvalue = tbcol.DbDefaultValue;
 | 
			
		||||
                        sb.Append(insertvalue).Append(", ");
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n");
 | 
			
		||||
                    sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
 | 
			
		||||
                    sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
 | 
			
		||||
                    //创建表的索引
 | 
			
		||||
                    foreach (var uk in tb.Indexes)
 | 
			
		||||
                    {
 | 
			
		||||
                        sb.Append("CREATE ");
 | 
			
		||||
                        if (uk.IsUnique) sb.Append("UNIQUE ");
 | 
			
		||||
                        sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(tablename).Append("(");
 | 
			
		||||
                        foreach (var tbcol in uk.Columns)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
 | 
			
		||||
                            if (tbcol.IsDesc) sb.Append(" DESC");
 | 
			
		||||
                            sb.Append(", ");
 | 
			
		||||
                        }
 | 
			
		||||
                        sb.Remove(sb.Length - 2, 2).Append(");\r\n");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return sb.Length == 0 ? null : sb.ToString();
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(database) == false)
 | 
			
		||||
                        conn.Value.ChangeDatabase(database);
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(conn);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(conn, true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            object LocalExecuteScalar(string db, string sql)
 | 
			
		||||
            {
 | 
			
		||||
                if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    using (var cmd = conn.Value.CreateCommand())
 | 
			
		||||
                    {
 | 
			
		||||
                        cmd.CommandText = sql;
 | 
			
		||||
                        cmd.CommandType = CommandType.Text;
 | 
			
		||||
                        return cmd.ExecuteScalar();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteDDLStatements(string ddl)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(ddl)) return 0;
 | 
			
		||||
            var scripts = ddl.Split(new string[] { ";\r\n" }, StringSplitOptions.None).Where(a => string.IsNullOrEmpty(a.Trim()) == false).ToArray();
 | 
			
		||||
 | 
			
		||||
            if (scripts.Any() == false) return 0;
 | 
			
		||||
 | 
			
		||||
            var affrows = 0;
 | 
			
		||||
            foreach (var script in scripts)
 | 
			
		||||
                affrows += base.ExecuteDDLStatements(script);
 | 
			
		||||
            return affrows;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										457
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlDbFirst.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										457
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlDbFirst.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,457 @@
 | 
			
		||||
using FreeSql.DatabaseModel;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
    class CustomMySqlDbFirst : IDbFirst
 | 
			
		||||
    {
 | 
			
		||||
        IFreeSql _orm;
 | 
			
		||||
        protected CommonUtils _commonUtils;
 | 
			
		||||
        protected CommonExpression _commonExpression;
 | 
			
		||||
        public CustomMySqlDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
            _orm = orm;
 | 
			
		||||
            _commonUtils = commonUtils;
 | 
			
		||||
            _commonExpression = commonExpression;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetDbType(DbColumnInfo column) => (int)GetSqlDbType(column);
 | 
			
		||||
        DbType GetSqlDbType(DbColumnInfo column)
 | 
			
		||||
        {
 | 
			
		||||
            var is_unsigned = column.DbTypeTextFull?.ToLower().EndsWith(" unsigned") == true;
 | 
			
		||||
            switch (column.DbTypeText?.ToLower())
 | 
			
		||||
            {
 | 
			
		||||
                case "bit": return DbType.Boolean;
 | 
			
		||||
 | 
			
		||||
                case "tinyint": return is_unsigned ? DbType.Byte : DbType.SByte;
 | 
			
		||||
                case "smallint": return is_unsigned ? DbType.Int16 : DbType.Int16;
 | 
			
		||||
                case "mediumint": return is_unsigned ? DbType.Int32 : DbType.Int32;
 | 
			
		||||
                case "int": return is_unsigned ? DbType.Int32 : DbType.Int32;
 | 
			
		||||
                case "bigint": return is_unsigned ? DbType.UInt64 : DbType.Int64;
 | 
			
		||||
 | 
			
		||||
                case "real":
 | 
			
		||||
                case "double": return DbType.Double;
 | 
			
		||||
                case "float": return DbType.Single;
 | 
			
		||||
                case "numeric":
 | 
			
		||||
                case "decimal": return DbType.Decimal;
 | 
			
		||||
 | 
			
		||||
                case "year": return DbType.Int32;
 | 
			
		||||
                case "time": return DbType.Time;
 | 
			
		||||
                case "date": return DbType.Date;
 | 
			
		||||
                case "timestamp": return DbType.DateTime;
 | 
			
		||||
                case "datetime": return DbType.DateTime;
 | 
			
		||||
 | 
			
		||||
                case "tinyblob": return DbType.Binary;
 | 
			
		||||
                case "blob": return DbType.Binary;
 | 
			
		||||
                case "mediumblob": return DbType.Binary;
 | 
			
		||||
                case "longblob": return DbType.Binary;
 | 
			
		||||
 | 
			
		||||
                case "binary": return DbType.Binary;
 | 
			
		||||
                case "varbinary": return DbType.Binary;
 | 
			
		||||
 | 
			
		||||
                case "tinytext": return DbType.String;
 | 
			
		||||
                case "text": return DbType.String;
 | 
			
		||||
                case "mediumtext": return DbType.String;
 | 
			
		||||
                case "longtext": return DbType.String;
 | 
			
		||||
 | 
			
		||||
                case "char": return column.MaxLength == 36 ? DbType.Guid : DbType.AnsiString;
 | 
			
		||||
                case "varchar": return DbType.String;
 | 
			
		||||
 | 
			
		||||
                case "set": return DbType.String;
 | 
			
		||||
                case "enum": return DbType.String;
 | 
			
		||||
 | 
			
		||||
                default: return DbType.String;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static readonly Dictionary<int, DbToCs> _dicDbToCs = new Dictionary<int, DbToCs>() {
 | 
			
		||||
                { (int)DbType.Boolean, new DbToCs("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.SByte, new DbToCs("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { (int)DbType.Int16, new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { (int)DbType.Int32, new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { (int)DbType.Int64, new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Byte, new DbToCs("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
 | 
			
		||||
                { (int)DbType.UInt16, new DbToCs("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { (int)DbType.UInt32, new DbToCs("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt64") },
 | 
			
		||||
                { (int)DbType.UInt64, new DbToCs("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Double, new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
 | 
			
		||||
                { (int)DbType.Single, new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
 | 
			
		||||
                { (int)DbType.Decimal, new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Time, new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
 | 
			
		||||
                { (int)DbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
                { (int)DbType.DateTime, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.Binary, new DbToCs("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.String, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
 | 
			
		||||
                { (int)DbType.AnsiString, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
                { (int)DbType.Guid, new DbToCs("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
 | 
			
		||||
        public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
 | 
			
		||||
        public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
 | 
			
		||||
        public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
 | 
			
		||||
        public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
 | 
			
		||||
        public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
 | 
			
		||||
        public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
 | 
			
		||||
 | 
			
		||||
        public List<string> GetDatabases()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = @" select schema_name from information_schema.schemata where schema_name not in ('information_schema', 'mysql', 'performance_schema')";
 | 
			
		||||
            var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool ExistsTable(string name, bool ignoreCase)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(name)) return false;
 | 
			
		||||
            var tbname = _commonUtils.SplitTableName(name);
 | 
			
		||||
            if (tbname?.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                var database = "";
 | 
			
		||||
                using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
 | 
			
		||||
                {
 | 
			
		||||
                    database = conn.Value.Database;
 | 
			
		||||
                }
 | 
			
		||||
                tbname = new[] { database, tbname[0] };
 | 
			
		||||
            }
 | 
			
		||||
            if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
 | 
			
		||||
            var sql = $" SELECT 1 FROM information_schema.TABLES WHERE {(ignoreCase ? "lower(table_schema)" : "table_schema")} = {_commonUtils.FormatSql("{0}", tbname[0])} and {(ignoreCase ? "lower(table_name)" : "table_name")} = {_commonUtils.FormatSql("{0}", tbname[1])}";
 | 
			
		||||
            return string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, sql)) == "1";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DbTableInfo GetTableByName(string name, bool ignoreCase = true) => GetTables(null, name, ignoreCase)?.FirstOrDefault();
 | 
			
		||||
        public List<DbTableInfo> GetTablesByDatabase(params string[] database) => GetTables(database, null, false);
 | 
			
		||||
 | 
			
		||||
        public List<DbTableInfo> GetTables(string[] database, string tablename, bool ignoreCase)
 | 
			
		||||
        {
 | 
			
		||||
            var loc1 = new List<DbTableInfo>();
 | 
			
		||||
            var loc2 = new Dictionary<string, DbTableInfo>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
            var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
            string[] tbname = null;
 | 
			
		||||
            if (string.IsNullOrEmpty(tablename) == false)
 | 
			
		||||
            {
 | 
			
		||||
                tbname = _commonUtils.SplitTableName(tablename);
 | 
			
		||||
                if (tbname?.Length == 1)
 | 
			
		||||
                {
 | 
			
		||||
                    using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.IsNullOrEmpty(conn.Value.Database)) return loc1;
 | 
			
		||||
                        tbname = new[] { conn.Value.Database, tbname[0] };
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
 | 
			
		||||
                database = new[] { tbname[0] };
 | 
			
		||||
            }
 | 
			
		||||
            else if (database == null || database.Any() == false)
 | 
			
		||||
            {
 | 
			
		||||
                using (var conn = _orm.Ado.MasterPool.Get())
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(conn.Value.Database)) return loc1;
 | 
			
		||||
                    database = new[] { conn.Value.Database };
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var databaseIn = string.Join(",", database.Select(a => _commonUtils.FormatSql("{0}", a)));
 | 
			
		||||
            var sql = $@"
 | 
			
		||||
select 
 | 
			
		||||
concat(a.table_schema, '.', a.table_name) 'id',
 | 
			
		||||
a.table_schema 'schema',
 | 
			
		||||
a.table_name 'table',
 | 
			
		||||
a.table_comment,
 | 
			
		||||
a.table_type 'type'
 | 
			
		||||
from information_schema.tables a
 | 
			
		||||
where {(ignoreCase ? "lower(a.table_schema)" : "a.table_schema")} in ({databaseIn}){(tbname == null ? "" : $" and {(ignoreCase ? "lower(a.table_name)" : "a.table_name")}={_commonUtils.FormatSql("{0}", tbname[1])}")}";
 | 
			
		||||
            var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
            var loc6 = new List<string[]>();
 | 
			
		||||
            var loc66 = new List<string[]>();
 | 
			
		||||
            var loc6_1000 = new List<string>();
 | 
			
		||||
            var loc66_1000 = new List<string>();
 | 
			
		||||
            foreach (var row in ds)
 | 
			
		||||
            {
 | 
			
		||||
                var table_id = string.Concat(row[0]);
 | 
			
		||||
                var schema = string.Concat(row[1]);
 | 
			
		||||
                var table = string.Concat(row[2]);
 | 
			
		||||
                var comment = string.Concat(row[3]);
 | 
			
		||||
                var type = string.Concat(row[4]) == "VIEW" ? DbTableType.VIEW : DbTableType.TABLE;
 | 
			
		||||
                if (database.Length == 1)
 | 
			
		||||
                {
 | 
			
		||||
                    table_id = table_id.Substring(table_id.IndexOf('.') + 1);
 | 
			
		||||
                    schema = "";
 | 
			
		||||
                }
 | 
			
		||||
                loc2.Add(table_id, new DbTableInfo { Id = table_id, Schema = schema, Name = table, Comment = comment, Type = type });
 | 
			
		||||
                loc3.Add(table_id, new Dictionary<string, DbColumnInfo>(StringComparer.CurrentCultureIgnoreCase));
 | 
			
		||||
                switch (type)
 | 
			
		||||
                {
 | 
			
		||||
                    case DbTableType.TABLE:
 | 
			
		||||
                    case DbTableType.VIEW:
 | 
			
		||||
                        loc6_1000.Add(table.Replace("'", "''"));
 | 
			
		||||
                        if (loc6_1000.Count >= 500)
 | 
			
		||||
                        {
 | 
			
		||||
                            loc6.Add(loc6_1000.ToArray());
 | 
			
		||||
                            loc6_1000.Clear();
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case DbTableType.StoreProcedure:
 | 
			
		||||
                        loc66_1000.Add(table.Replace("'", "''"));
 | 
			
		||||
                        if (loc66_1000.Count >= 500)
 | 
			
		||||
                        {
 | 
			
		||||
                            loc66.Add(loc66_1000.ToArray());
 | 
			
		||||
                            loc66_1000.Clear();
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (loc6_1000.Count > 0) loc6.Add(loc6_1000.ToArray());
 | 
			
		||||
            if (loc66_1000.Count > 0) loc66.Add(loc66_1000.ToArray());
 | 
			
		||||
 | 
			
		||||
            if (loc6.Count == 0) return loc1;
 | 
			
		||||
            var loc8 = new StringBuilder().Append("(");
 | 
			
		||||
            for (var loc8idx = 0; loc8idx < loc6.Count; loc8idx++)
 | 
			
		||||
            {
 | 
			
		||||
                if (loc8idx > 0) loc8.Append(" OR ");
 | 
			
		||||
                loc8.Append("a.table_name in (");
 | 
			
		||||
                for (var loc8idx2 = 0; loc8idx2 < loc6[loc8idx].Length; loc8idx2++)
 | 
			
		||||
                {
 | 
			
		||||
                    if (loc8idx2 > 0) loc8.Append(",");
 | 
			
		||||
                    loc8.Append($"'{loc6[loc8idx][loc8idx2]}'");
 | 
			
		||||
                }
 | 
			
		||||
                loc8.Append(")");
 | 
			
		||||
            }
 | 
			
		||||
            loc8.Append(")");
 | 
			
		||||
 | 
			
		||||
            sql = $@"
 | 
			
		||||
select
 | 
			
		||||
concat(a.table_schema, '.', a.table_name),
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.data_type,
 | 
			
		||||
ifnull(a.character_maximum_length, 0) 'len',
 | 
			
		||||
a.column_type,
 | 
			
		||||
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
 | 
			
		||||
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
 | 
			
		||||
a.column_comment 'comment',
 | 
			
		||||
a.column_default 'default_value',
 | 
			
		||||
a.ordinal_position
 | 
			
		||||
from information_schema.columns a
 | 
			
		||||
where {(ignoreCase ? "lower(a.table_schema)" : "a.table_schema")} in ({databaseIn}) and {loc8}
 | 
			
		||||
";
 | 
			
		||||
            ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
            foreach (var row in ds)
 | 
			
		||||
            {
 | 
			
		||||
                string table_id = string.Concat(row[0]);
 | 
			
		||||
                string column = string.Concat(row[1]);
 | 
			
		||||
                string type = string.Concat(row[2]);
 | 
			
		||||
                //long max_length = long.Parse(string.Concat(row[3]));
 | 
			
		||||
                string sqlType = string.Concat(row[4]);
 | 
			
		||||
                var m_len = Regex.Match(sqlType, @"\w+\((\d+)");
 | 
			
		||||
                int max_length = m_len.Success ? int.Parse(m_len.Groups[1].Value) : -1;
 | 
			
		||||
                bool is_nullable = string.Concat(row[5]) == "1";
 | 
			
		||||
                bool is_identity = string.Concat(row[6]) == "1";
 | 
			
		||||
                string comment = string.Concat(row[7]);
 | 
			
		||||
                string defaultValue = string.Concat(row[8]);
 | 
			
		||||
                var position = int.Parse(string.Concat(row[9]));
 | 
			
		||||
 | 
			
		||||
                if (max_length == 0) max_length = -1;
 | 
			
		||||
                if (database.Length == 1)
 | 
			
		||||
                {
 | 
			
		||||
                    table_id = table_id.Substring(table_id.IndexOf('.') + 1);
 | 
			
		||||
                }
 | 
			
		||||
                loc3[table_id].Add(column, new DbColumnInfo
 | 
			
		||||
                {
 | 
			
		||||
                    Name = column,
 | 
			
		||||
                    MaxLength = max_length,
 | 
			
		||||
                    IsIdentity = is_identity,
 | 
			
		||||
                    IsNullable = is_nullable,
 | 
			
		||||
                    IsPrimary = false,
 | 
			
		||||
                    DbTypeText = type,
 | 
			
		||||
                    DbTypeTextFull = sqlType,
 | 
			
		||||
                    Table = loc2[table_id],
 | 
			
		||||
                    Comment = comment,
 | 
			
		||||
                    DefaultValue = defaultValue,
 | 
			
		||||
                    Position = position
 | 
			
		||||
                });
 | 
			
		||||
                loc3[table_id][column].DbType = this.GetDbType(loc3[table_id][column]);
 | 
			
		||||
                loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sql = $@"
 | 
			
		||||
select 
 | 
			
		||||
concat(a.table_schema, '.', a.table_name) 'table_id',
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.index_name 'index_id',
 | 
			
		||||
case when a.non_unique = 0 then 1 else 0 end 'IsUnique',
 | 
			
		||||
case when a.index_name = 'PRIMARY' then 1 else 0 end 'IsPrimaryKey',
 | 
			
		||||
0 'IsClustered',
 | 
			
		||||
0 'IsDesc'
 | 
			
		||||
from information_schema.statistics a
 | 
			
		||||
where {(ignoreCase ? "lower(a.table_schema)" : "a.table_schema")} in ({databaseIn}) and {loc8}
 | 
			
		||||
";
 | 
			
		||||
            ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
            var indexColumns = new Dictionary<string, Dictionary<string, DbIndexInfo>>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
            var uniqueColumns = new Dictionary<string, Dictionary<string, DbIndexInfo>>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
            foreach (var row in ds)
 | 
			
		||||
            {
 | 
			
		||||
                string table_id = string.Concat(row[0]);
 | 
			
		||||
                string column = string.Concat(row[1]);
 | 
			
		||||
                string index_id = string.Concat(row[2]);
 | 
			
		||||
                bool is_unique = string.Concat(row[3]) == "1";
 | 
			
		||||
                bool is_primary_key = string.Concat(row[4]) == "1";
 | 
			
		||||
                bool is_clustered = string.Concat(row[5]) == "1";
 | 
			
		||||
                bool is_desc = string.Concat(row[6]) == "1";
 | 
			
		||||
                if (database.Length == 1)
 | 
			
		||||
                    table_id = table_id.Substring(table_id.IndexOf('.') + 1);
 | 
			
		||||
                if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
 | 
			
		||||
                var loc9 = loc3[table_id][column];
 | 
			
		||||
                if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
 | 
			
		||||
 | 
			
		||||
                Dictionary<string, DbIndexInfo> loc10 = null;
 | 
			
		||||
                DbIndexInfo loc11 = null;
 | 
			
		||||
                if (!indexColumns.TryGetValue(table_id, out loc10))
 | 
			
		||||
                    indexColumns.Add(table_id, loc10 = new Dictionary<string, DbIndexInfo>(StringComparer.CurrentCultureIgnoreCase));
 | 
			
		||||
                if (!loc10.TryGetValue(index_id, out loc11))
 | 
			
		||||
                    loc10.Add(index_id, loc11 = new DbIndexInfo());
 | 
			
		||||
                loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc });
 | 
			
		||||
                if (is_unique && !is_primary_key)
 | 
			
		||||
                {
 | 
			
		||||
                    if (!uniqueColumns.TryGetValue(table_id, out loc10))
 | 
			
		||||
                        uniqueColumns.Add(table_id, loc10 = new Dictionary<string, DbIndexInfo>(StringComparer.CurrentCultureIgnoreCase));
 | 
			
		||||
                    if (!loc10.TryGetValue(index_id, out loc11))
 | 
			
		||||
                        loc10.Add(index_id, loc11 = new DbIndexInfo());
 | 
			
		||||
                    loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            foreach (string table_id in indexColumns.Keys)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var column in indexColumns[table_id])
 | 
			
		||||
                    loc2[table_id].IndexesDict.Add(column.Key, column.Value);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (string table_id in uniqueColumns.Keys)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var column in uniqueColumns[table_id])
 | 
			
		||||
                {
 | 
			
		||||
                    column.Value.Columns.Sort((c1, c2) => c1.Column.Name.CompareTo(c2.Column.Name));
 | 
			
		||||
                    loc2[table_id].UniquesDict.Add(column.Key, column.Value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (tbname == null)
 | 
			
		||||
            {
 | 
			
		||||
                sql = $@"
 | 
			
		||||
select 
 | 
			
		||||
concat(a.constraint_schema, '.', a.table_name) 'table_id',
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.constraint_name 'FKId',
 | 
			
		||||
concat(a.referenced_table_schema, '.', a.referenced_table_name) 'ref_table_id',
 | 
			
		||||
1 'IsForeignKey',
 | 
			
		||||
a.referenced_column_name 'ref_column'
 | 
			
		||||
from information_schema.key_column_usage a
 | 
			
		||||
where {(ignoreCase ? "lower(a.constraint_schema)" : "a.constraint_schema")} in ({databaseIn}) and {loc8} and not isnull(position_in_unique_constraint)
 | 
			
		||||
";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
                foreach (var row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    string table_id = string.Concat(row[0]);
 | 
			
		||||
                    string column = string.Concat(row[1]);
 | 
			
		||||
                    string fk_id = string.Concat(row[2]);
 | 
			
		||||
                    string ref_table_id = string.Concat(row[3]);
 | 
			
		||||
                    bool is_foreign_key = string.Concat(row[4]) == "1";
 | 
			
		||||
                    string referenced_column = string.Concat(row[5]);
 | 
			
		||||
                    if (database.Length == 1)
 | 
			
		||||
                    {
 | 
			
		||||
                        table_id = table_id.Substring(table_id.IndexOf('.') + 1);
 | 
			
		||||
                        ref_table_id = ref_table_id.Substring(ref_table_id.IndexOf('.') + 1);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
 | 
			
		||||
                    var loc9 = loc3[table_id][column];
 | 
			
		||||
                    if (loc2.ContainsKey(ref_table_id) == false) continue;
 | 
			
		||||
                    var loc10 = loc2[ref_table_id];
 | 
			
		||||
                    var loc11 = loc3[ref_table_id][referenced_column];
 | 
			
		||||
 | 
			
		||||
                    Dictionary<string, DbForeignInfo> loc12 = null;
 | 
			
		||||
                    DbForeignInfo loc13 = null;
 | 
			
		||||
                    if (!fkColumns.TryGetValue(table_id, out loc12))
 | 
			
		||||
                        fkColumns.Add(table_id, loc12 = new Dictionary<string, DbForeignInfo>(StringComparer.CurrentCultureIgnoreCase));
 | 
			
		||||
                    if (!loc12.TryGetValue(fk_id, out loc13))
 | 
			
		||||
                        loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[table_id], ReferencedTable = loc10 });
 | 
			
		||||
                    loc13.Columns.Add(loc9);
 | 
			
		||||
                    loc13.ReferencedColumns.Add(loc11);
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var table_id in fkColumns.Keys)
 | 
			
		||||
                    foreach (var fk in fkColumns[table_id])
 | 
			
		||||
                        loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var table_id in loc3.Keys)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var loc5 in loc3[table_id].Values)
 | 
			
		||||
                {
 | 
			
		||||
                    loc2[table_id].Columns.Add(loc5);
 | 
			
		||||
                    if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
 | 
			
		||||
                    if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var loc4 in loc2.Values)
 | 
			
		||||
            {
 | 
			
		||||
                //if (loc4.Primarys.Count == 0 && loc4.UniquesDict.Count > 0)
 | 
			
		||||
                //{
 | 
			
		||||
                //    foreach (var loc5 in loc4.UniquesDict.First().Value.Columns)
 | 
			
		||||
                //    {
 | 
			
		||||
                //        loc5.Column.IsPrimary = true;
 | 
			
		||||
                //        loc4.Primarys.Add(loc5.Column);
 | 
			
		||||
                //    }
 | 
			
		||||
                //}
 | 
			
		||||
                loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
 | 
			
		||||
                loc4.Columns.Sort((c1, c2) =>
 | 
			
		||||
                {
 | 
			
		||||
                    int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
 | 
			
		||||
                    if (compare == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        bool b1 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
 | 
			
		||||
                        bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
 | 
			
		||||
                        compare = b2.CompareTo(b1);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
 | 
			
		||||
                    return compare;
 | 
			
		||||
                });
 | 
			
		||||
                loc1.Add(loc4);
 | 
			
		||||
            }
 | 
			
		||||
            loc1.Sort((t1, t2) =>
 | 
			
		||||
            {
 | 
			
		||||
                var ret = t1.Schema.CompareTo(t2.Schema);
 | 
			
		||||
                if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
 | 
			
		||||
                return ret;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            loc2.Clear();
 | 
			
		||||
            loc3.Clear();
 | 
			
		||||
            return loc1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
 | 
			
		||||
        {
 | 
			
		||||
            return new List<DbEnumInfo>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										563
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlExpression.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										563
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlExpression.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,563 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
    class CustomMySqlExpression : CommonExpression
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlExpression(CommonUtils common) : base(common) { }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            switch (exp.NodeType)
 | 
			
		||||
            {
 | 
			
		||||
                case ExpressionType.ArrayLength:
 | 
			
		||||
                    var arrOper = (exp as UnaryExpression)?.Operand;
 | 
			
		||||
                    if (arrOper.Type == typeof(byte[])) return $"octet_length({getExp(arrOper)})";
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.Convert:
 | 
			
		||||
                    var operandExp = (exp as UnaryExpression)?.Operand;
 | 
			
		||||
                    var gentype = exp.Type.NullableTypeOrThis();
 | 
			
		||||
                    if (gentype != operandExp.Type.NullableTypeOrThis())
 | 
			
		||||
                    {
 | 
			
		||||
                        switch (gentype.ToString())
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
 | 
			
		||||
                            case "System.Byte": return $"cast({getExp(operandExp)} as unsigned)";
 | 
			
		||||
                            case "System.Char": return $"substr(cast({getExp(operandExp)} as char), 1, 1)";
 | 
			
		||||
                            case "System.DateTime": return ExpressionConstDateTime(operandExp) ?? $"cast({getExp(operandExp)} as datetime)";
 | 
			
		||||
                            case "System.Decimal": return $"cast({getExp(operandExp)} as decimal(36,18))";
 | 
			
		||||
                            case "System.Double": return $"cast({getExp(operandExp)} as decimal(32,16))";
 | 
			
		||||
                            case "System.Int16":
 | 
			
		||||
                            case "System.Int32":
 | 
			
		||||
                            case "System.Int64":
 | 
			
		||||
                            case "System.SByte": return $"cast({getExp(operandExp)} as signed)";
 | 
			
		||||
                            case "System.Single": return $"cast({getExp(operandExp)} as decimal(14,7))";
 | 
			
		||||
                            case "System.String": return $"cast({getExp(operandExp)} as char)";
 | 
			
		||||
                            case "System.UInt16":
 | 
			
		||||
                            case "System.UInt32":
 | 
			
		||||
                            case "System.UInt64": return $"cast({getExp(operandExp)} as unsigned)";
 | 
			
		||||
                            case "System.Guid": return $"substr(cast({getExp(operandExp)} as char), 1, 36)";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.Call:
 | 
			
		||||
                    var callExp = exp as MethodCallExpression;
 | 
			
		||||
 | 
			
		||||
                    switch (callExp.Method.Name)
 | 
			
		||||
                    {
 | 
			
		||||
                        case "Parse":
 | 
			
		||||
                        case "TryParse":
 | 
			
		||||
                            switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
 | 
			
		||||
                            {
 | 
			
		||||
                                case "System.Boolean": return $"({getExp(callExp.Arguments[0])} not in ('0','false'))";
 | 
			
		||||
                                case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as unsigned)";
 | 
			
		||||
                                case "System.Char": return $"substr(cast({getExp(callExp.Arguments[0])} as char), 1, 1)";
 | 
			
		||||
                                case "System.DateTime": return ExpressionConstDateTime(callExp.Arguments[0]) ?? $"cast({getExp(callExp.Arguments[0])} as datetime)";
 | 
			
		||||
                                case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as decimal(36,18))";
 | 
			
		||||
                                case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as decimal(32,16))";
 | 
			
		||||
                                case "System.Int16":
 | 
			
		||||
                                case "System.Int32":
 | 
			
		||||
                                case "System.Int64":
 | 
			
		||||
                                case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as signed)";
 | 
			
		||||
                                case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as decimal(14,7))";
 | 
			
		||||
                                case "System.UInt16":
 | 
			
		||||
                                case "System.UInt32":
 | 
			
		||||
                                case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as unsigned)";
 | 
			
		||||
                                case "System.Guid": return $"substr(cast({getExp(callExp.Arguments[0])} as char), 1, 36)";
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "NewGuid":
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "Next":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "cast(rand()*1000000000 as signed)";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "NextDouble":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "rand()";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "Random":
 | 
			
		||||
                            if (callExp.Method.DeclaringType.IsNumberType()) return "rand()";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "ToString":
 | 
			
		||||
                            if (callExp.Object != null) return callExp.Arguments.Count == 0 ? $"cast({getExp(callExp.Object)} as char)" : null;
 | 
			
		||||
                            return null;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var objExp = callExp.Object;
 | 
			
		||||
                    var objType = objExp?.Type;
 | 
			
		||||
                    if (objType?.FullName == "System.Byte[]") return null;
 | 
			
		||||
 | 
			
		||||
                    var argIndex = 0;
 | 
			
		||||
                    if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable))
 | 
			
		||||
                    {
 | 
			
		||||
                        objExp = callExp.Arguments.FirstOrDefault();
 | 
			
		||||
                        objType = objExp?.Type;
 | 
			
		||||
                        argIndex++;
 | 
			
		||||
 | 
			
		||||
                        if (objType == typeof(string))
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (callExp.Method.Name)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "First":
 | 
			
		||||
                                case "FirstOrDefault":
 | 
			
		||||
                                    return $"substr({getExp(callExp.Arguments[0])}, 1, 1)";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (objType == null) objType = callExp.Method.DeclaringType;
 | 
			
		||||
                    if (objType != null || objType.IsArrayOrList())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (argIndex >= callExp.Arguments.Count) break;
 | 
			
		||||
                        tsc.SetMapColumnTmp(null);
 | 
			
		||||
                        var args1 = getExp(callExp.Arguments[argIndex]);
 | 
			
		||||
                        var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
 | 
			
		||||
                        var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
 | 
			
		||||
                        tsc.isNotSetMapColumnTmp = true;
 | 
			
		||||
                        var left = objExp == null ? null : getExp(objExp);
 | 
			
		||||
                        tsc.isNotSetMapColumnTmp = false;
 | 
			
		||||
                        tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
 | 
			
		||||
                        if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
 | 
			
		||||
                        switch (callExp.Method.Name)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "Contains":
 | 
			
		||||
                                //判断 in //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
 | 
			
		||||
                                return $"(({args1}) in {left.Replace(",   \r\n    \r\n", $") \r\n OR ({args1}) in (")})";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.NewArrayInit:
 | 
			
		||||
                    var arrExp = exp as NewArrayExpression;
 | 
			
		||||
                    var arrSb = new StringBuilder();
 | 
			
		||||
                    arrSb.Append("(");
 | 
			
		||||
                    for (var a = 0; a < arrExp.Expressions.Count; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (a > 0) arrSb.Append(",");
 | 
			
		||||
                        if (a % 500 == 499) arrSb.Append("   \r\n    \r\n"); //500元素分割, 3空格\r\n4空格
 | 
			
		||||
                        arrSb.Append(getExp(arrExp.Expressions[a]));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (arrSb.Length == 1) arrSb.Append("NULL");
 | 
			
		||||
                    return arrSb.Append(")").ToString();
 | 
			
		||||
                case ExpressionType.ListInit:
 | 
			
		||||
                    var listExp = exp as ListInitExpression;
 | 
			
		||||
                    var listSb = new StringBuilder();
 | 
			
		||||
                    listSb.Append("(");
 | 
			
		||||
                    for (var a = 0; a < listExp.Initializers.Count; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (listExp.Initializers[a].Arguments.Any() == false) continue;
 | 
			
		||||
                        if (a > 0) listSb.Append(",");
 | 
			
		||||
                        listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (listSb.Length == 1) listSb.Append("NULL");
 | 
			
		||||
                    return listSb.Append(")").ToString();
 | 
			
		||||
                case ExpressionType.New:
 | 
			
		||||
                    var newExp = exp as NewExpression;
 | 
			
		||||
                    if (typeof(IList).IsAssignableFrom(newExp.Type))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (newExp.Arguments.Count == 0) return "(NULL)";
 | 
			
		||||
                        if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
 | 
			
		||||
                        return getExp(newExp.Arguments[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                    return null;
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Empty": return "''";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Length": return $"char_length({left})";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Now": return _common.Now;
 | 
			
		||||
                    case "UtcNow": return _common.NowUtc;
 | 
			
		||||
                    case "Today": return "curdate()";
 | 
			
		||||
                    case "MinValue": return "cast('0001/1/1 0:00:00' as datetime)";
 | 
			
		||||
                    case "MaxValue": return "cast('9999/12/31 23:59:59' as datetime)";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Date": return $"cast(date_format({left},'%Y-%m-%d') as datetime)";
 | 
			
		||||
                case "TimeOfDay": return $"timestampdiff(microsecond, date_format({left},'%Y-%m-%d'), {left})";
 | 
			
		||||
                case "DayOfWeek": return $"(dayofweek({left})-1)";
 | 
			
		||||
                case "Day": return $"dayofmonth({left})";
 | 
			
		||||
                case "DayOfYear": return $"dayofyear({left})";
 | 
			
		||||
                case "Month": return $"month({left})";
 | 
			
		||||
                case "Year": return $"year({left})";
 | 
			
		||||
                case "Hour": return $"hour({left})";
 | 
			
		||||
                case "Minute": return $"minute({left})";
 | 
			
		||||
                case "Second": return $"second({left})";
 | 
			
		||||
                case "Millisecond": return $"floor(microsecond({left})/1000)";
 | 
			
		||||
                case "Ticks": return $"(timestampdiff(microsecond, '0001-1-1', {left})*10)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Zero": return "0";
 | 
			
		||||
                    case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
 | 
			
		||||
                    case "MaxValue": return "922337203685477580";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Days": return $"(({left}) div {(long)1000000 * 60 * 60 * 24})";
 | 
			
		||||
                case "Hours": return $"(({left}) div {(long)1000000 * 60 * 60} mod 24)";
 | 
			
		||||
                case "Milliseconds": return $"(({left}) div 1000 mod 1000)";
 | 
			
		||||
                case "Minutes": return $"(({left}) div {(long)1000000 * 60} mod 60)";
 | 
			
		||||
                case "Seconds": return $"(({left}) div 1000000 mod 60)";
 | 
			
		||||
                case "Ticks": return $"(({left}) * 10)";
 | 
			
		||||
                case "TotalDays": return $"(({left}) / {(long)1000000 * 60 * 60 * 24})";
 | 
			
		||||
                case "TotalHours": return $"(({left}) / {(long)1000000 * 60 * 60})";
 | 
			
		||||
                case "TotalMilliseconds": return $"(({left}) / 1000)";
 | 
			
		||||
                case "TotalMinutes": return $"(({left}) / {(long)1000000 * 60})";
 | 
			
		||||
                case "TotalSeconds": return $"(({left}) / 1000000)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "IsNullOrEmpty":
 | 
			
		||||
                        var arg1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"({arg1} is null or {arg1} = '')";
 | 
			
		||||
                    case "IsNullOrWhiteSpace":
 | 
			
		||||
                        var arg2 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')";
 | 
			
		||||
                    case "Concat":
 | 
			
		||||
                        return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
 | 
			
		||||
                    case "Format":
 | 
			
		||||
                        if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception(CoreStrings.Not_Implemented_Expression_ParameterUseConstant(exp,exp.Arguments[0]));
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return ExpressionLambdaToSql(exp.Arguments[0], tsc);
 | 
			
		||||
                        var expArgsHack = exp.Arguments.Count == 2 && exp.Arguments[1].NodeType == ExpressionType.NewArrayInit ?
 | 
			
		||||
                            (exp.Arguments[1] as NewArrayExpression).Expressions : exp.Arguments.Where((a, z) => z > 0);
 | 
			
		||||
                        //3个 {} 时,Arguments 解析出来是分开的
 | 
			
		||||
                        //4个 {} 时,Arguments[1] 只能解析这个出来,然后里面是 NewArray []
 | 
			
		||||
                        var expArgs = expArgsHack.Select(a => $"',{_common.IsNull(ExpressionLambdaToSql(a, tsc), "''")},'").ToArray();
 | 
			
		||||
                        return $"concat({string.Format(ExpressionLambdaToSql(exp.Arguments[0], tsc), expArgs)})";
 | 
			
		||||
                    case "Join":
 | 
			
		||||
                        if (exp.IsStringJoin(out var tolistObjectExp, out var toListMethod, out var toListArgs1))
 | 
			
		||||
                        {
 | 
			
		||||
                            var newToListArgs0 = Expression.Call(tolistObjectExp, toListMethod,
 | 
			
		||||
                                Expression.Lambda(
 | 
			
		||||
                                    Expression.Call(
 | 
			
		||||
                                        typeof(SqlExtExtensions).GetMethod("StringJoinMySqlGroupConcat"),
 | 
			
		||||
                                        Expression.Convert(toListArgs1.Body, typeof(object)),
 | 
			
		||||
                                        Expression.Convert(exp.Arguments[0], typeof(object))),
 | 
			
		||||
                                    toListArgs1.Parameters));
 | 
			
		||||
                            var newToListSql = getExp(newToListArgs0);
 | 
			
		||||
                            return newToListSql;
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "StartsWith":
 | 
			
		||||
                    case "EndsWith":
 | 
			
		||||
                    case "Contains":
 | 
			
		||||
                        var args0Value = getExp(exp.Arguments[0]);
 | 
			
		||||
                        if (args0Value == "NULL") return $"({left}) IS NULL";
 | 
			
		||||
                        if (args0Value.Contains("%"))
 | 
			
		||||
                        {
 | 
			
		||||
                            if (exp.Method.Name == "StartsWith") return $"locate({args0Value}, {left}) = 1";
 | 
			
		||||
                            if (exp.Method.Name == "EndsWith") return $"locate({args0Value}, {left}) = char_length({args0Value})";
 | 
			
		||||
                            return $"locate({args0Value}, {left}) > 0";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
 | 
			
		||||
                        if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
 | 
			
		||||
                        if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
 | 
			
		||||
                        return $"({left}) LIKE concat('%', {args0Value}, '%')";
 | 
			
		||||
                    case "ToLower": return $"lower({left})";
 | 
			
		||||
                    case "ToUpper": return $"upper({left})";
 | 
			
		||||
                    case "Substring":
 | 
			
		||||
                        var substrArgs1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
 | 
			
		||||
                        else substrArgs1 += "+1";
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})";
 | 
			
		||||
                        return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "IndexOf":
 | 
			
		||||
                        var indexOfFindStr = getExp(exp.Arguments[0]);
 | 
			
		||||
                        if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32")
 | 
			
		||||
                        {
 | 
			
		||||
                            var locateArgs1 = getExp(exp.Arguments[1]);
 | 
			
		||||
                            if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
 | 
			
		||||
                            else locateArgs1 += "+1";
 | 
			
		||||
                            return $"(locate({indexOfFindStr}, {left}, {locateArgs1})-1)";
 | 
			
		||||
                        }
 | 
			
		||||
                        return $"(locate({indexOfFindStr}, {left})-1)";
 | 
			
		||||
                    case "PadLeft":
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
 | 
			
		||||
                        return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "PadRight":
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
 | 
			
		||||
                        return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "Trim":
 | 
			
		||||
                    case "TrimStart":
 | 
			
		||||
                    case "TrimEnd":
 | 
			
		||||
                        if (exp.Arguments.Count == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (exp.Method.Name == "Trim") return $"trim({left})";
 | 
			
		||||
                            if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
 | 
			
		||||
                            if (exp.Method.Name == "TrimEnd") return $"rtrim({left})";
 | 
			
		||||
                        }
 | 
			
		||||
                        foreach (var argsTrim02 in exp.Arguments)
 | 
			
		||||
                        {
 | 
			
		||||
                            var argsTrim01s = new[] { argsTrim02 };
 | 
			
		||||
                            if (argsTrim02.NodeType == ExpressionType.NewArrayInit)
 | 
			
		||||
                            {
 | 
			
		||||
                                var arritem = argsTrim02 as NewArrayExpression;
 | 
			
		||||
                                argsTrim01s = arritem.Expressions.ToArray();
 | 
			
		||||
                            }
 | 
			
		||||
                            foreach (var argsTrim01 in argsTrim01s)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (exp.Method.Name == "Trim") left = $"trim({getExp(argsTrim01)} from {left})";
 | 
			
		||||
                                if (exp.Method.Name == "TrimStart") left = $"trim(leading {getExp(argsTrim01)} from {left})";
 | 
			
		||||
                                if (exp.Method.Name == "TrimEnd") left = $"trim(trailing {getExp(argsTrim01)} from {left})";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return left;
 | 
			
		||||
                    case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "CompareTo": return $"strcmp({left}, {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            switch (exp.Method.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Abs": return $"abs({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Sign": return $"sign({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Floor": return $"floor({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Round":
 | 
			
		||||
                    if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    return $"round({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Exp": return $"exp({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log": return $"log({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log10": return $"log10({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Cos": return $"cos({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Sin": return $"sin({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Tan": return $"tan({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Acos": return $"acos({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Asin": return $"asin({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Atan": return $"atan({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                case "Truncate": return $"truncate({getExp(exp.Arguments[0])}, 0)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))";
 | 
			
		||||
                    case "DaysInMonth": return $"dayofmonth(last_day(concat({getExp(exp.Arguments[0])}, '-', {getExp(exp.Arguments[1])}, '-01')))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
 | 
			
		||||
                    case "IsLeapYear":
 | 
			
		||||
                        var isLeapYearArgs1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
 | 
			
		||||
 | 
			
		||||
                    case "Parse": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as datetime)";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as datetime)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"date_add({left}, interval ({args1}) microsecond)";
 | 
			
		||||
                    case "AddDays": return $"date_add({left}, interval ({args1}) day)";
 | 
			
		||||
                    case "AddHours": return $"date_add({left}, interval ({args1}) hour)";
 | 
			
		||||
                    case "AddMilliseconds": return $"date_add({left}, interval ({args1})*1000 microsecond)";
 | 
			
		||||
                    case "AddMinutes": return $"date_add({left}, interval ({args1}) minute)";
 | 
			
		||||
                    case "AddMonths": return $"date_add({left}, interval ({args1}) month)";
 | 
			
		||||
                    case "AddSeconds": return $"date_add({left}, interval ({args1}) second)";
 | 
			
		||||
                    case "AddTicks": return $"date_add({left}, interval ({args1})/10 microsecond)";
 | 
			
		||||
                    case "AddYears": return $"date_add({left}, interval ({args1}) year)";
 | 
			
		||||
                    case "Subtract":
 | 
			
		||||
                        switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.DateTime": return $"timestampdiff(microsecond, {args1}, {left})";
 | 
			
		||||
                            case "System.TimeSpan": return $"date_sub({left}, interval ({args1}) microsecond)";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"timestampdiff(microsecond,{args1},{left})";
 | 
			
		||||
                    case "ToString":
 | 
			
		||||
                        if (exp.Arguments.Count == 0) return $"date_format({left},'%Y-%m-%d %H:%i:%s.%f')";
 | 
			
		||||
                        switch (args1)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "'yyyy-MM-dd HH:mm:ss'": return $"date_format({left},'%Y-%m-%d %H:%i:%s')";
 | 
			
		||||
                            case "'yyyy-MM-dd HH:mm'": return $"date_format({left},'%Y-%m-%d %H:%i')";
 | 
			
		||||
                            case "'yyyy-MM-dd HH'": return $"date_format({left},'%Y-%m-%d %H')";
 | 
			
		||||
                            case "'yyyy-MM-dd'": return $"date_format({left},'%Y-%m-%d')";
 | 
			
		||||
                            case "'yyyy-MM'": return $"date_format({left},'%Y-%m')";
 | 
			
		||||
                            case "'yyyyMMddHHmmss'": return $"date_format({left},'%Y%m%d%H%i%s')";
 | 
			
		||||
                            case "'yyyyMMddHHmm'": return $"date_format({left},'%Y%m%d%H%i')";
 | 
			
		||||
                            case "'yyyyMMddHH'": return $"date_format({left},'%Y%m%d%H')";
 | 
			
		||||
                            case "'yyyyMMdd'": return $"date_format({left},'%Y%m%d')";
 | 
			
		||||
                            case "'yyyyMM'": return $"date_format({left},'%Y%m')";
 | 
			
		||||
                            case "'yyyy'": return $"date_format({left},'%Y')";
 | 
			
		||||
                            case "'HH:mm:ss'": return $"date_format({left},'%H:%i:%s')";
 | 
			
		||||
                        }
 | 
			
		||||
                        args1 = Regex.Replace(args1, "(yyyy|yy|MM|M|dd|d|HH|H|hh|h|mm|ss|tt)", m =>
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (m.Groups[1].Value)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "yyyy": return $"%Y";
 | 
			
		||||
                                case "yy": return $"%y";
 | 
			
		||||
                                case "MM": return $"%_a1";
 | 
			
		||||
                                case "M": return $"%c";
 | 
			
		||||
                                case "dd": return $"%d";
 | 
			
		||||
                                case "d": return $"%e";
 | 
			
		||||
                                case "HH": return $"%H";
 | 
			
		||||
                                case "H": return $"%k";
 | 
			
		||||
                                case "hh": return $"%h";
 | 
			
		||||
                                case "h": return $"%l";
 | 
			
		||||
                                case "mm": return $"%i";
 | 
			
		||||
                                case "ss": return $"%_a2";
 | 
			
		||||
                                case "tt": return $"%p";
 | 
			
		||||
                            }
 | 
			
		||||
                            return m.Groups[0].Value;
 | 
			
		||||
                        });
 | 
			
		||||
                        var argsFinds = new[] { "%Y", "%y", "%_a1", "%c", "%d", "%e", "%H", "%k", "%h", "%l", "%i", "%_a2", "%p" };
 | 
			
		||||
                        var argsSpts = Regex.Split(args1, "(m|s|t)");
 | 
			
		||||
                        for (var a = 0; a < argsSpts.Length; a++)
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (argsSpts[a])
 | 
			
		||||
                            {
 | 
			
		||||
                                case "m": argsSpts[a] = $"case when substr(date_format({left},'%i'),1,1) = '0' then substr(date_format({left},'%i'),2,1) else date_format({left},'%i') end"; break;
 | 
			
		||||
                                case "s": argsSpts[a] = $"case when substr(date_format({left},'%s'),1,1) = '0' then substr(date_format({left},'%s'),2,1) else date_format({left},'%s') end"; break;
 | 
			
		||||
                                case "t": argsSpts[a] = $"trim(trailing 'M' from date_format({left},'%p'))"; break;
 | 
			
		||||
                                default:
 | 
			
		||||
                                    var argsSptsA = argsSpts[a];
 | 
			
		||||
                                    if (argsSptsA.StartsWith("'")) argsSptsA = argsSptsA.Substring(1);
 | 
			
		||||
                                    if (argsSptsA.EndsWith("'")) argsSptsA = argsSptsA.Remove(argsSptsA.Length - 1);
 | 
			
		||||
                                    argsSpts[a] = argsFinds.Any(m => argsSptsA.Contains(m)) ? $"date_format({left},'{argsSptsA}')" : $"'{argsSptsA}'";
 | 
			
		||||
                                    break;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (argsSpts.Length > 0) args1 = $"concat({string.Join(", ", argsSpts.Where(a => a != "''"))})";
 | 
			
		||||
                        return args1.Replace("%_a1", "%m").Replace("%_a2", "%s");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})";
 | 
			
		||||
                    case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})";
 | 
			
		||||
                    case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)";
 | 
			
		||||
                    case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})";
 | 
			
		||||
                    case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)";
 | 
			
		||||
                    case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)";
 | 
			
		||||
                    case "Parse": return $"cast({getExp(exp.Arguments[0])} as signed)";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as signed)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({args1}))";
 | 
			
		||||
                    case "ToString": return $"cast({left} as char)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
 | 
			
		||||
                    case "ToByte": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
 | 
			
		||||
                    case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as char), 1, 1)";
 | 
			
		||||
                    case "ToDateTime": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as datetime)";
 | 
			
		||||
                    case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
 | 
			
		||||
                    case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))";
 | 
			
		||||
                    case "ToInt16":
 | 
			
		||||
                    case "ToInt32":
 | 
			
		||||
                    case "ToInt64":
 | 
			
		||||
                    case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as signed)";
 | 
			
		||||
                    case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))";
 | 
			
		||||
                    case "ToString": return $"cast({getExp(exp.Arguments[0])} as char)";
 | 
			
		||||
                    case "ToUInt16":
 | 
			
		||||
                    case "ToUInt32":
 | 
			
		||||
                    case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,46 @@
 | 
			
		||||
using FreeSql.Internal.CommonProvider;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class CustomMySqlProvider<TMark> : BaseDbProvider, IFreeSql<TMark>
 | 
			
		||||
    {
 | 
			
		||||
        public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) => new CustomMySqlSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IInsert<T1> CreateInsertProvider<T1>() => new CustomMySqlInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere) => new CustomMySqlUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere) => new CustomMySqlDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>() => new CustomMySqlInsertOrUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlProvider(string masterConnectionString, string[] slaveConnectionString, Func<DbConnection> connectionFactory = null)
 | 
			
		||||
        {
 | 
			
		||||
            this.InternalCommonUtils = new CustomMySqlUtils(this);
 | 
			
		||||
            this.InternalCommonExpression = new CustomMySqlExpression(this.InternalCommonUtils);
 | 
			
		||||
 | 
			
		||||
            this.Ado = new CustomMySqlAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory);
 | 
			
		||||
            this.Aop = new AopProvider();
 | 
			
		||||
 | 
			
		||||
            this.DbFirst = new CustomMySqlDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
            this.CodeFirst = new CustomMySqlCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ~CustomMySqlProvider() => this.Dispose();
 | 
			
		||||
        int _disposeCounter;
 | 
			
		||||
        public override void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            if (Interlocked.Increment(ref _disposeCounter) != 1) return;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                (this.Ado as AdoProvider)?.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                FreeSqlCustomAdapterGlobalExtensions._dicCustomAdater.TryRemove(Ado.Identifier, out var tryada);
 | 
			
		||||
                FreeSqlCustomAdapterGlobalExtensions._dicDbProviderFactory.TryRemove(Ado.Identifier, out var trydbpf);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlUtils.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								Providers/FreeSql.Provider.Custom/MySql/CustomMySqlUtils.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.MySql
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomMySqlUtils : CommonUtils
 | 
			
		||||
    {
 | 
			
		||||
        DbProviderFactory Factory => FreeSqlCustomAdapterGlobalExtensions.GetDbProviderFactory(_orm);
 | 
			
		||||
 | 
			
		||||
        public CustomMySqlUtils(IFreeSql orm) : base(orm)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, ColumnInfo col, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
 | 
			
		||||
            var ret = Factory.CreateParameter();
 | 
			
		||||
            ret.ParameterName = QuoteParamterName(parameterName);
 | 
			
		||||
            var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
            if (tp != null) ret.DbType = (DbType)tp.Value;
 | 
			
		||||
            ret.Value = value;
 | 
			
		||||
            _params?.Add(ret);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
 | 
			
		||||
            Utils.GetDbParamtersByObject<DbParameter>(sql, obj, null, (name, type, value) =>
 | 
			
		||||
            {
 | 
			
		||||
                var ret = Factory.CreateParameter();
 | 
			
		||||
                ret.ParameterName = $"@{name}";
 | 
			
		||||
                var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
                if (tp != null) ret.DbType = (DbType)tp.Value;
 | 
			
		||||
                ret.Value = value;
 | 
			
		||||
                return ret;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomMySql(args);
 | 
			
		||||
        public override string QuoteSqlName(params string[] name)
 | 
			
		||||
        {
 | 
			
		||||
            if (name.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                var nametrim = name[0].Trim();
 | 
			
		||||
                if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                    return nametrim; //原生SQL
 | 
			
		||||
                if (nametrim.StartsWith("`") && nametrim.EndsWith("`"))
 | 
			
		||||
                    return nametrim;
 | 
			
		||||
                return $"`{nametrim.Replace(".", "`.`")}`";
 | 
			
		||||
            }
 | 
			
		||||
            return $"`{string.Join("`.`", name)}`";
 | 
			
		||||
        }
 | 
			
		||||
        public override string TrimQuoteSqlName(string name)
 | 
			
		||||
        {
 | 
			
		||||
            var nametrim = name.Trim();
 | 
			
		||||
            if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                return nametrim; //原生SQL
 | 
			
		||||
            return $"{nametrim.Trim('`').Replace("`.`", ".").Replace(".`", ".")}";
 | 
			
		||||
        }
 | 
			
		||||
        public override string[] SplitTableName(string name) => GetSplitTableNames(name, '`', '`', 2);
 | 
			
		||||
        public override string QuoteParamterName(string name) => $"@{name}";
 | 
			
		||||
        public override string IsNull(string sql, object value) => $"ifnull({sql}, {value})";
 | 
			
		||||
        public override string StringConcat(string[] objs, Type[] types) => $"concat({string.Join(", ", objs)})";
 | 
			
		||||
        public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
 | 
			
		||||
        public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} div {right}";
 | 
			
		||||
        public override string Now => "now()";
 | 
			
		||||
        public override string NowUtc => "utc_timestamp()";
 | 
			
		||||
 | 
			
		||||
        public override string QuoteWriteParamterAdapter(Type type, string paramterName)
 | 
			
		||||
        {
 | 
			
		||||
            switch (type.FullName)
 | 
			
		||||
            {
 | 
			
		||||
                case "MygisPoint":
 | 
			
		||||
                case "MygisLineString":
 | 
			
		||||
                case "MygisPolygon":
 | 
			
		||||
                case "MygisMultiPoint":
 | 
			
		||||
                case "MygisMultiLineString":
 | 
			
		||||
                case "MygisMultiPolygon": return $"ST_GeomFromText({paramterName})";
 | 
			
		||||
            }
 | 
			
		||||
            return paramterName;
 | 
			
		||||
        }
 | 
			
		||||
        protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName)
 | 
			
		||||
        {
 | 
			
		||||
            switch (mapType.FullName)
 | 
			
		||||
            {
 | 
			
		||||
                case "MygisPoint":
 | 
			
		||||
                case "MygisLineString":
 | 
			
		||||
                case "MygisPolygon":
 | 
			
		||||
                case "MygisMultiPoint":
 | 
			
		||||
                case "MygisMultiLineString":
 | 
			
		||||
                case "MygisMultiPolygon": return $"ST_AsText({columnName})";
 | 
			
		||||
            }
 | 
			
		||||
            return columnName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, string specialParamFlag, ColumnInfo col, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (value == null) return "NULL";
 | 
			
		||||
            if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value);
 | 
			
		||||
            if (type == typeof(byte[])) return $"0x{CommonUtils.BytesSqlRaw(value as byte[])}";
 | 
			
		||||
            if (type == typeof(TimeSpan) || type == typeof(TimeSpan?))
 | 
			
		||||
            {
 | 
			
		||||
                var ts = (TimeSpan)value;
 | 
			
		||||
                value = $"{Math.Floor(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}";
 | 
			
		||||
            }
 | 
			
		||||
            return FormatSql("{0}", value, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user