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,26 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleDelete<T1> : Internal.CommonProvider.DeleteProvider<T1>
 | 
			
		||||
    {
 | 
			
		||||
        public CustomOracleDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override List<T1> ExecuteDeleted() => throw new NotImplementedException($"FreeSql.Odbc.Oracle {CoreStrings.S_Not_Implemented_Feature}");
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException($"FreeSql.Odbc.Oracle {CoreStrings.S_Not_Implemented_Feature}");
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,224 @@
 | 
			
		||||
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.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public CustomOracleInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 999);
 | 
			
		||||
        public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 999);
 | 
			
		||||
        public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 999);
 | 
			
		||||
 | 
			
		||||
        public override string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            if (_source == null || _source.Any() == false) return null;
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append("INSERT ");
 | 
			
		||||
            if (_source.Count > 1) sb.Append("ALL");
 | 
			
		||||
 | 
			
		||||
            _identCol = null;
 | 
			
		||||
            var sbtb = new StringBuilder();
 | 
			
		||||
            sbtb.Append("INTO ");
 | 
			
		||||
            sbtb.Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append("(");
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (col.Attribute.IsIdentity) _identCol = col;
 | 
			
		||||
                if (col.Attribute.IsIdentity && _insertIdentity == false && string.IsNullOrEmpty(col.DbInsertValue)) continue;
 | 
			
		||||
                if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name)) continue;
 | 
			
		||||
 | 
			
		||||
                if (colidx > 0) sbtb.Append(", ");
 | 
			
		||||
                sbtb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sbtb.Append(") ");
 | 
			
		||||
 | 
			
		||||
            _params = _noneParameter ? new DbParameter[0] : new DbParameter[colidx * _source.Count];
 | 
			
		||||
            var specialParams = new List<DbParameter>();
 | 
			
		||||
            var didx = 0;
 | 
			
		||||
            foreach (var d in _source)
 | 
			
		||||
            {
 | 
			
		||||
                if (_source.Count > 1) sb.Append("\r\n");
 | 
			
		||||
                sb.Append(sbtb);
 | 
			
		||||
                sb.Append("VALUES");
 | 
			
		||||
                sb.Append("(");
 | 
			
		||||
                var colidx2 = 0;
 | 
			
		||||
                foreach (var col in _table.Columns.Values)
 | 
			
		||||
                {
 | 
			
		||||
                    if (col.Attribute.IsIdentity && _insertIdentity == false && string.IsNullOrEmpty(col.DbInsertValue)) continue;
 | 
			
		||||
                    if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name)) continue;
 | 
			
		||||
 | 
			
		||||
                    if (colidx2 > 0) sb.Append(", ");
 | 
			
		||||
                    if (string.IsNullOrEmpty(col.DbInsertValue) == false)
 | 
			
		||||
                        sb.Append(col.DbInsertValue);
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        object val = col.GetDbValue(d);
 | 
			
		||||
                        if (val == null && col.Attribute.IsNullable == false) val = col.CsType == typeof(string) ? "" : Utils.GetDataReaderValue(col.CsType.NullableTypeOrThis(), null);//#384
 | 
			
		||||
 | 
			
		||||
                        var colsql = _noneParameter ? _commonUtils.GetNoneParamaterSqlValue(specialParams, _noneParameterFlag, col, col.Attribute.MapType, val) :
 | 
			
		||||
                            _commonUtils.QuoteWriteParamterAdapter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"{col.CsName}_{didx}"));
 | 
			
		||||
                        sb.Append(_commonUtils.RewriteColumn(col, colsql));
 | 
			
		||||
                        if (_noneParameter == false)
 | 
			
		||||
                            _params[didx * colidx + colidx2] = _commonUtils.AppendParamter(null, $"{col.CsName}_{didx}", col, col.Attribute.MapType, val);
 | 
			
		||||
                    }
 | 
			
		||||
                    ++colidx2;
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append(")");
 | 
			
		||||
                ++didx;
 | 
			
		||||
            }
 | 
			
		||||
            if (_noneParameter && specialParams.Any()) _params = specialParams.ToArray();
 | 
			
		||||
            if (_source.Count > 1) sb.Append("\r\n SELECT 1 FROM DUAL");
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ColumnInfo _identCol;
 | 
			
		||||
        protected override long RawExecuteIdentity()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            Aop.CurdBeforeEventArgs before = null;
 | 
			
		||||
 | 
			
		||||
            if (_identCol == null || _source.Count > 1)
 | 
			
		||||
            {
 | 
			
		||||
                before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret = _orm.Ado.ExecuteNonQuery(_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 0;
 | 
			
		||||
            }
 | 
			
		||||
            var identColName = _commonUtils.QuoteSqlName(_identCol.Attribute.Name);
 | 
			
		||||
            var identParam = _commonUtils.AppendParamter(null, $"{_identCol.CsName}99", _identCol, _identCol.Attribute.MapType, 0);
 | 
			
		||||
            identParam.Direction = ParameterDirection.Output;
 | 
			
		||||
            sql = $"{sql} RETURNING {identColName} INTO {identParam.ParameterName}";
 | 
			
		||||
            var dbParms = _params.Concat(new[] { identParam }).ToArray();
 | 
			
		||||
            before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, dbParms);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms);
 | 
			
		||||
                long.TryParse(string.Concat(identParam.Value), out ret);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                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 ret = _source.ToList();
 | 
			
		||||
            this.RawExecuteAffrows();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 999, cancellationToken);
 | 
			
		||||
        public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 999, cancellationToken);
 | 
			
		||||
        public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 999, cancellationToken);
 | 
			
		||||
 | 
			
		||||
        async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            Aop.CurdBeforeEventArgs before = null;
 | 
			
		||||
 | 
			
		||||
            if (_identCol == null || _source.Count > 1)
 | 
			
		||||
            {
 | 
			
		||||
                before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
                _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret = await _orm.Ado.ExecuteNonQueryAsync(_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 0;
 | 
			
		||||
            }
 | 
			
		||||
            var identColName = _commonUtils.QuoteSqlName(_identCol.Attribute.Name);
 | 
			
		||||
            var identParam = _commonUtils.AppendParamter(null, $"{_identCol.CsName}99", _identCol, _identCol.Attribute.MapType, 0);
 | 
			
		||||
            identParam.Direction = ParameterDirection.Output;
 | 
			
		||||
            sql = $"{sql} RETURNING {identColName} INTO {identParam.ParameterName}";
 | 
			
		||||
            var dbParms = _params.Concat(new[] { identParam }).ToArray();
 | 
			
		||||
            before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, dbParms);
 | 
			
		||||
            _orm.Aop.CurdBeforeHandler?.Invoke(this, before);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken);
 | 
			
		||||
                long.TryParse(string.Concat(identParam.Value), out ret);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                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 ret = _source.ToList();
 | 
			
		||||
            await this.RawExecuteAffrowsAsync(cancellationToken);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,74 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleInsertOrUpdate<T1> : Internal.CommonProvider.InsertOrUpdateProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public CustomOracleInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToSql()
 | 
			
		||||
        {
 | 
			
		||||
            var dbParams = new List<DbParameter>();
 | 
			
		||||
            if (_sourceSql != null) return getMergeSql(null);
 | 
			
		||||
            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 => getMergeSql(a)));
 | 
			
		||||
            if (ds.Item2.Any()) sqls[1] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item2.Select(a => getInsertSql(a)));
 | 
			
		||||
            _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 getMergeSql(List<T1> data)
 | 
			
		||||
            {
 | 
			
		||||
                if (_tempPrimarys.Any() == false) throw new Exception(CoreStrings.InsertOrUpdate_Must_Primary_Key(_table.CsName));
 | 
			
		||||
 | 
			
		||||
                var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\nUSING (");
 | 
			
		||||
                WriteSourceSelectUnionAll(data, sb, dbParams);
 | 
			
		||||
                sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _tempPrimarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"))).Append(") \r\n");
 | 
			
		||||
 | 
			
		||||
                var cols = _table.Columns.Values.Where(a => _tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
 | 
			
		||||
                if (_doNothing == false && cols.Any())
 | 
			
		||||
                    sb.Append("WHEN MATCHED THEN \r\n")
 | 
			
		||||
                        .Append("  update set ").Append(string.Join(", ", cols.Select(a =>
 | 
			
		||||
                            a.Attribute.IsVersion && a.Attribute.MapType != typeof(byte[]) ?
 | 
			
		||||
                            $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" :
 | 
			
		||||
                            $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"
 | 
			
		||||
                            ))).Append(" \r\n");
 | 
			
		||||
 | 
			
		||||
                cols = _table.Columns.Values.Where(a => a.Attribute.CanInsert == true);
 | 
			
		||||
                if (cols.Any())
 | 
			
		||||
                    sb.Append("WHEN NOT MATCHED THEN \r\n")
 | 
			
		||||
                        .Append("  insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n")
 | 
			
		||||
                        .Append("  values (").Append(string.Join(", ", cols.Select(a => $"t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"))).Append(")");
 | 
			
		||||
 | 
			
		||||
                return sb.ToString();
 | 
			
		||||
            }
 | 
			
		||||
            string getInsertSql(List<T1> data)
 | 
			
		||||
            {
 | 
			
		||||
                var insert = _orm.Insert<T1>()
 | 
			
		||||
                    .AsTable(_tableRule).AsType(_table.Type)
 | 
			
		||||
                    .WithConnection(_connection)
 | 
			
		||||
                    .WithTransaction(_transaction)
 | 
			
		||||
                    .NoneParameter(true) as Internal.CommonProvider.InsertProvider<T1>;
 | 
			
		||||
                insert._source = data;
 | 
			
		||||
                insert._table = _table;
 | 
			
		||||
                var sql = insert.ToSql();
 | 
			
		||||
                if (string.IsNullOrEmpty(sql)) return null;
 | 
			
		||||
                if (insert._params?.Any() == true) dbParams.AddRange(insert._params);
 | 
			
		||||
                return sql;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,232 @@
 | 
			
		||||
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.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleSelect<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 sbunion = new StringBuilder();
 | 
			
		||||
            var sbnav = 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");
 | 
			
		||||
                var tbUnion = tbUnions[tbUnionsIdx];
 | 
			
		||||
 | 
			
		||||
                sbunion.Append(_select);
 | 
			
		||||
                if (_distinct) sbunion.Append("DISTINCT ");
 | 
			
		||||
                sbunion.Append(field);
 | 
			
		||||
                if (string.IsNullOrEmpty(_orderby) && _skip > 0) sbunion.Append(", ROWNUM AS \"__rownum__\"");
 | 
			
		||||
                sbunion.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++)
 | 
			
		||||
                {
 | 
			
		||||
                    sbunion.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++)
 | 
			
		||||
                        {
 | 
			
		||||
                            sbunion.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)) sbunion.Append(" ON 1 = 1");
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
 | 
			
		||||
                                sbunion.Append(" ON ").Append(onSql);
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (string.IsNullOrEmpty(onSql)) sbunion.Append(tbsfrom[b].Cascade);
 | 
			
		||||
                                    else sbunion.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) sbunion.Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var tb in tbsjoin)
 | 
			
		||||
                {
 | 
			
		||||
                    switch (tb.Type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case SelectTableInfoType.Parent:
 | 
			
		||||
                        case SelectTableInfoType.RawJoin:
 | 
			
		||||
                            continue;
 | 
			
		||||
                        case SelectTableInfoType.LeftJoin:
 | 
			
		||||
                            sbunion.Append(" \r\nLEFT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.InnerJoin:
 | 
			
		||||
                            sbunion.Append(" \r\nINNER JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.RightJoin:
 | 
			
		||||
                            sbunion.Append(" \r\nRIGHT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    sbunion.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)) sbunion.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) sbunion.Append(_join);
 | 
			
		||||
 | 
			
		||||
                sbnav.Append(_where);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_tables[0].Cascade))
 | 
			
		||||
                    sbnav.Append(" AND ").Append(_tables[0].Cascade);
 | 
			
		||||
 | 
			
		||||
                if (string.IsNullOrEmpty(_orderby) && (_skip > 0 || _limit > 0))
 | 
			
		||||
                    sbnav.Append(" AND ROWNUM < ").Append(_skip + _limit + 1);
 | 
			
		||||
                if (sbnav.Length > 0)
 | 
			
		||||
                    sbunion.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
 | 
			
		||||
                if (string.IsNullOrEmpty(_groupby) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sbunion.Append(_groupby);
 | 
			
		||||
                    if (string.IsNullOrEmpty(_having) == false)
 | 
			
		||||
                        sbunion.Append(" \r\nHAVING ").Append(_having.Substring(5));
 | 
			
		||||
                }
 | 
			
		||||
                sbunion.Append(_orderby);
 | 
			
		||||
 | 
			
		||||
                if (string.IsNullOrEmpty(_orderby))
 | 
			
		||||
                {
 | 
			
		||||
                    if (_skip > 0)
 | 
			
		||||
                        sbunion.Insert(0, $"{_select}t.* FROM (").Append(") t WHERE t.\"__rownum__\" > ").Append(_skip);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (_skip > 0 && _limit > 0) sbunion.Insert(0, $"{_select}t.* FROM (SELECT rt.*, ROWNUM AS \"__rownum__\" FROM (").Append(") rt WHERE ROWNUM < ").Append(_skip + _limit + 1).Append(") t WHERE t.\"__rownum__\" > ").Append(_skip);
 | 
			
		||||
                    else if (_skip > 0) sbunion.Insert(0, $"{_select}t.* FROM (").Append(") t WHERE ROWNUM > ").Append(_skip);
 | 
			
		||||
                    else if (_limit > 0) sbunion.Insert(0, $"{_select}t.* FROM (").Append(") t WHERE ROWNUM < ").Append(_limit + 1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (tbUnionsGt0) sbunion.Insert(0, $"{_select}* from (").Append(") ftb");
 | 
			
		||||
                sb.Append(sbunion);
 | 
			
		||||
                sbnav.Clear();
 | 
			
		||||
                sbunion.Clear();
 | 
			
		||||
            }
 | 
			
		||||
            var sql = sb.Append(_tosqlAppendContent).ToString();
 | 
			
		||||
 | 
			
		||||
            var aliasGreater30 = 0;
 | 
			
		||||
            foreach (var tb in _tables)
 | 
			
		||||
                if (tb.Alias.Length > 30) sql = sql.Replace(tb.Alias, $"than30_{aliasGreater30++}");
 | 
			
		||||
 | 
			
		||||
            return sql;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CustomOracleSelect(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 OdbcOracleSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm, _commonUtils, _commonExpression, null); CustomOracleSelect<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 OdbcOracleSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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 OdbcOracleSelect<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 OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => CustomOracleSelect<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,74 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public CustomOracleUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 200, _batchParameterLimit > 0 ? _batchParameterLimit : 999);
 | 
			
		||||
        public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 200, _batchParameterLimit > 0 ? _batchParameterLimit : 999);
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteUpdated() => throw new NotImplementedException($"FreeSql.Odbc.Oracle {CoreStrings.S_Not_Implemented_Feature}");
 | 
			
		||||
 | 
			
		||||
        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("(");
 | 
			
		||||
            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)
 | 
			
		||||
            {
 | 
			
		||||
                if (primarys[0].Attribute.DbType.Contains("NVARCHAR2"))
 | 
			
		||||
                    sb.Append("N");
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            sb.Append("(");
 | 
			
		||||
            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 : 200, _batchParameterLimit > 0 ? _batchParameterLimit : 999, cancellationToken);
 | 
			
		||||
        public override Task<List<T1>> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 200, _batchParameterLimit > 0 ? _batchParameterLimit : 999, cancellationToken);
 | 
			
		||||
 | 
			
		||||
        protected override Task<List<T1>> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException($"FreeSql.Odbc.Oracle {CoreStrings.S_Not_Implemented_Feature}");
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,81 @@
 | 
			
		||||
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.Text.RegularExpressions;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
    class CustomOracleAdo : FreeSql.Internal.CommonProvider.AdoProvider
 | 
			
		||||
    {
 | 
			
		||||
        DbProviderFactory Factory => FreeSqlCustomAdapterGlobalExtensions.GetDbProviderFactory(_util._orm);
 | 
			
		||||
 | 
			
		||||
        public CustomOracleAdo() : base(DataType.OdbcOracle, null, null) { }
 | 
			
		||||
        public CustomOracleAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.OdbcOracle, masterConnectionString, slaveConnectionStrings)
 | 
			
		||||
        {
 | 
			
		||||
            base._util = util;
 | 
			
		||||
            if (connectionFactory != null)
 | 
			
		||||
            {
 | 
			
		||||
                var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.SqlServer, connectionFactory);
 | 
			
		||||
                MasterPool = pool;
 | 
			
		||||
                using (var conn = pool.Get())
 | 
			
		||||
                    UserId = CustomOracleAdo.GetUserId(conn.Value.ConnectionString);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            throw new Exception(CoreStrings.S_CustomAdapter_OnlySuppport_UseConnectionFactory);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal string UserId { get; set; }
 | 
			
		||||
        public static string GetUserId(string connectionString)
 | 
			
		||||
        {
 | 
			
		||||
            var userIdMatch = Regex.Match(connectionString, @"(User\s+Id|Uid)\s*=\s*([^;]+)", RegexOptions.IgnoreCase);
 | 
			
		||||
            if (userIdMatch.Success == false) throw new Exception(@"从 ConnectionString 中无法匹配 (User\s+Id|Uid)\s*=\s*([^;]+)");
 | 
			
		||||
            return userIdMatch.Groups[2].Value.Trim().ToUpper();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 byte[])
 | 
			
		||||
                return $"hextoraw('{CommonUtils.BytesSqlRaw(param as byte[])}')";
 | 
			
		||||
            else if (param is bool || param is bool?)
 | 
			
		||||
                return (bool)param ? 1 : 0;
 | 
			
		||||
            else if (param is string)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
            else if (param is char)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''").Replace('\0', ' '), "'");
 | 
			
		||||
            else if (param is Enum)
 | 
			
		||||
                return ((Enum)param).ToInt64();
 | 
			
		||||
            else if (decimal.TryParse(string.Concat(param), out var trydec))
 | 
			
		||||
                return param;
 | 
			
		||||
            else if (param is DateTime || param is DateTime?)
 | 
			
		||||
                return string.Concat("to_timestamp('", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "','YYYY-MM-DD HH24:MI:SS.FF6')");
 | 
			
		||||
            else if (param is TimeSpan || param is TimeSpan?)
 | 
			
		||||
                return $"numtodsinterval({((TimeSpan)param).Ticks * 1.0 / 10000000},'second')";
 | 
			
		||||
            else if (param is IEnumerable)
 | 
			
		||||
                return AddslashesIEnumerable(param, mapType, mapColumn);
 | 
			
		||||
 | 
			
		||||
            return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
            //if (param is string) return string.Concat('N', nparms[a]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,503 @@
 | 
			
		||||
using FreeSql.DataAnnotations;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleCodeFirst : Internal.CommonProvider.CodeFirstProvider
 | 
			
		||||
    {
 | 
			
		||||
        public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
 | 
			
		||||
        public CustomOracleCodeFirst(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, "number","number(1) NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DbType.Boolean, "number","number(1) NULL", null, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "number", "number(4) NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "number", "number(4) NULL", false, true, null) },
 | 
			
		||||
                { typeof(short).FullName, CsToDb.New(DbType.Int16, "number","number(6) NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, "number", "number(6) NULL", false, true, null) },
 | 
			
		||||
                { typeof(int).FullName, CsToDb.New(DbType.Int32, "number", "number(11) NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, "number", "number(11) NULL", false, true, null) },
 | 
			
		||||
                { typeof(long).FullName, CsToDb.New(DbType.Int64, "number","number(21) NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, "number","number(21) NULL", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte).FullName, CsToDb.New(DbType.Byte, "number","number(3) NOT NULL", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, "number","number(3) NULL", true, true, null) },
 | 
			
		||||
                { typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "number","number(5) NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "number", "number(5) NULL", true, true, null) },
 | 
			
		||||
                { typeof(uint).FullName, CsToDb.New(DbType.UInt32, "number", "number(10) NOT NULL", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "number", "number(10) NULL", true, true, null) },
 | 
			
		||||
                { typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "number", "number(20) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "number", "number(20) NULL", true, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(double).FullName, CsToDb.New(DbType.Double, "float", "float(126) NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, "float", "float(126) NULL", false, true, null) },
 | 
			
		||||
                { typeof(float).FullName, CsToDb.New(DbType.Single, "float","float(63) NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, "float","float(63) NULL", false, true, null) },
 | 
			
		||||
                { typeof(decimal).FullName, CsToDb.New(DbType.Decimal, "number", "number(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, "number", "number(10,2) NULL", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(TimeSpan).FullName, CsToDb.New(DbType.Time, "interval day to second","interval day(2) to second(6) NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(DbType.Time, "interval day to second", "interval day(2) to second(6) NULL",false, true, null) },
 | 
			
		||||
                { typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, "timestamp", "timestamp(6) NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, "timestamp", "timestamp(6) NULL", false, true, null) },
 | 
			
		||||
                { typeof(DateTimeOffset).FullName, CsToDb.New(DbType.DateTimeOffset, "timestamp with local time zone", "timestamp(6) with local time zone NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTimeOffset?).FullName, CsToDb.New(DbType.DateTimeOffset, "timestamp with local time zone", "timestamp(6) with local time zone NULL", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte[]).FullName, CsToDb.New(DbType.Binary, "blob", "blob NULL", false, null, new byte[0]) },
 | 
			
		||||
                { typeof(string).FullName, CsToDb.New(DbType.String, "nvarchar2", "nvarchar2(255) NULL", false, null, "") },
 | 
			
		||||
                { typeof(char).FullName, CsToDb.New(DbType.AnsiString, "char", "char(1 CHAR) NULL", false, null, '\0') },
 | 
			
		||||
 | 
			
		||||
                { typeof(Guid).FullName, CsToDb.New(DbType.Guid, "char", "char(36 CHAR) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(DbType.Guid, "char", "char(36 CHAR) NULL", 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 newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
 | 
			
		||||
                    CsToDb.New(DbType.Int32, "number", $"number(16){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()) :
 | 
			
		||||
                    CsToDb.New(DbType.Int64, "number", $"number(32){(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)
 | 
			
		||||
        {
 | 
			
		||||
            var userId = (_orm.Ado as CustomOracleAdo)?.UserId;
 | 
			
		||||
            if (string.IsNullOrEmpty(userId))
 | 
			
		||||
                using (var conn = _orm.Ado.MasterPool.Get())
 | 
			
		||||
                {
 | 
			
		||||
                    userId = CustomOracleAdo.GetUserId(conn.Value.ConnectionString);
 | 
			
		||||
                }
 | 
			
		||||
            var seqcols = new List<NativeTuple<ColumnInfo, string[], bool>>(); //序列:列,表,自增
 | 
			
		||||
            var seqnameDel = new List<string>(); //要删除的序列+触发器
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var sbDeclare = 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[] { userId, tbname[0] };
 | 
			
		||||
 | 
			
		||||
                var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名
 | 
			
		||||
                if (tboldname?.Length == 1) tboldname = new[] { userId, tboldname[0] };
 | 
			
		||||
                var primaryKeyName = (obj.entityType.GetCustomAttributes(typeof(OraclePrimaryKeyNameAttribute), false)?.FirstOrDefault() as OraclePrimaryKeyNameAttribute)?.Name;
 | 
			
		||||
                if (string.IsNullOrEmpty(obj.tableName) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    var tbtmpname = _commonUtils.SplitTableName(obj.tableName);
 | 
			
		||||
                    if (tbtmpname?.Length == 1) tbtmpname = new[] { userId, tbtmpname[0] };
 | 
			
		||||
                    if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1])
 | 
			
		||||
                    {
 | 
			
		||||
                        tbname = tbtmpname;
 | 
			
		||||
                        tboldname = null;
 | 
			
		||||
                        primaryKeyName = null;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                //codefirst 不支持表名中带 .
 | 
			
		||||
 | 
			
		||||
                if (string.Compare(tbname[0], userId) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from sys.dba_users where username={0}", tbname[0])) == null) //创建数据库
 | 
			
		||||
                    throw new NotImplementedException(CoreStrings.S_Oracle_NotSupport_TablespaceSchemas(tbname[0]));
 | 
			
		||||
 | 
			
		||||
                var sbalter = new StringBuilder();
 | 
			
		||||
                var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
 | 
			
		||||
                if (_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_tab_comments where owner={0} and table_name={1}", tbname)) == null)
 | 
			
		||||
                { //表不存在
 | 
			
		||||
                    if (tboldname != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_tab_comments where owner={0} and table_name={1}", tboldname)) == null)
 | 
			
		||||
                            //模式或表不存在
 | 
			
		||||
                            tboldname = null;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (tboldname == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        //创建表
 | 
			
		||||
                        var createTableName = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
 | 
			
		||||
                        sb.Append("execute immediate 'CREATE TABLE ").Append(createTableName).Append(" ( ");
 | 
			
		||||
                        foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
 | 
			
		||||
                            if (tbcol.Attribute.IsIdentity == true) seqcols.Add(NativeTuple.Create(tbcol, tbname, true));
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tb.Primarys.Any())
 | 
			
		||||
                        {
 | 
			
		||||
                            var pkname = primaryKeyName ?? $"{tbname[0]}_{tbname[1]}_pk1";
 | 
			
		||||
                            sb.Append(" \r\n  CONSTRAINT ").Append(_commonUtils.QuoteSqlName(pkname)).Append(" 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) \r\nLOGGING \r\nNOCOMPRESS \r\nNOCACHE\r\n';\r\n");
 | 
			
		||||
                        //创建表的索引
 | 
			
		||||
                        foreach (var uk in tb.Indexes)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append("execute immediate 'CREATE ");
 | 
			
		||||
                            if (uk.IsUnique) sb.Append("UNIQUE ");
 | 
			
		||||
                            sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(createTableName).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");
 | 
			
		||||
                        }
 | 
			
		||||
                        //备注
 | 
			
		||||
                        foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbcol.Comment) == false)
 | 
			
		||||
                                sb.Append("execute immediate 'COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment).Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
                        }
 | 
			
		||||
                        if (string.IsNullOrEmpty(tb.Comment) == false)
 | 
			
		||||
                            sb.Append("execute immediate 'COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment).Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    //如果新表,旧表在一个模式下,直接修改表名
 | 
			
		||||
                    if (string.Compare(tbname[0], tboldname[0], true) == 0)
 | 
			
		||||
                        sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append("';\r\n");
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果新表,旧表不在一起,创建新表,导入数据,删除旧表
 | 
			
		||||
                        istmpatler = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    tboldname = null; //如果新表已经存在,不走改表名逻辑
 | 
			
		||||
 | 
			
		||||
                //对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
 | 
			
		||||
                var sql = _commonUtils.FormatSql($@"
 | 
			
		||||
select 
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.data_type,
 | 
			
		||||
a.data_length,
 | 
			
		||||
a.data_precision,
 | 
			
		||||
a.data_scale,
 | 
			
		||||
a.char_used,
 | 
			
		||||
case when a.nullable = 'N' then 0 else 1 end,
 | 
			
		||||
nvl((select 1 from user_sequences where upper(sequence_name)=upper('{Utils.GetCsName((tboldname ?? tbname).Last())}_seq_'||a.column_name) and rownum < 2), 0),
 | 
			
		||||
nvl((select 1 from user_triggers where upper(trigger_name)=upper('{Utils.GetCsName((tboldname ?? tbname).Last())}_seq_'||a.column_name||'TI') and rownum < 2), 0),
 | 
			
		||||
b.comments
 | 
			
		||||
from all_tab_columns a
 | 
			
		||||
left join all_col_comments b on b.owner = a.owner and b.table_name = a.table_name and b.column_name = a.column_name
 | 
			
		||||
where a.owner={{0}} and a.table_name={{1}} and a.column_id is not null", tboldname ?? tbname);
 | 
			
		||||
                var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a =>
 | 
			
		||||
                {
 | 
			
		||||
                    var sqlType = GetOracleSqlTypeFullName(a);
 | 
			
		||||
                    return new
 | 
			
		||||
                    {
 | 
			
		||||
                        column = string.Concat(a[0]),
 | 
			
		||||
                        sqlType,
 | 
			
		||||
                        is_nullable = string.Concat(a[6]) == "1",
 | 
			
		||||
                        is_identity = string.Concat(a[7]) == "1" && string.Concat(a[8]) == "1",
 | 
			
		||||
                        comment = string.Concat(a[9])
 | 
			
		||||
                    };
 | 
			
		||||
                }, StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
 | 
			
		||||
                if (istmpatler == false)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        var dbtypeNoneNotNull = Regex.Replace(tbcol.Attribute.DbType, @"NOT\s+NULL", "NULL");
 | 
			
		||||
                        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 ?? "");
 | 
			
		||||
                            if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
 | 
			
		||||
                            {
 | 
			
		||||
                                istmpatler = true;
 | 
			
		||||
                                if (tbcol.Attribute.DbType.StartsWith("varchar", StringComparison.CurrentCultureIgnoreCase) && tbstructcol.sqlType.StartsWith("varchar2", StringComparison.CurrentCultureIgnoreCase)
 | 
			
		||||
                                    && Regex.Match(tbcol.Attribute.DbType, @"\(\d+").Groups[0].Value == Regex.Match(tbstructcol.sqlType, @"\(\d+").Groups[0].Value)
 | 
			
		||||
                                    istmpatler = false;
 | 
			
		||||
                                if (istmpatler) break;
 | 
			
		||||
                            }
 | 
			
		||||
                            //sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY (").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(dbtypeNoneNotNull).Append(")';\r\n");
 | 
			
		||||
                            if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (tbcol.Attribute.IsNullable == false)
 | 
			
		||||
                                    sbalter.Append("execute immediate 'UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" = ").Append(tbcol.DbDefaultValue.Replace("'", "''")).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" IS NULL';\r\n");
 | 
			
		||||
                                sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable == true ? "" : "NOT").Append(" NULL';\r\n");
 | 
			
		||||
                            }
 | 
			
		||||
                            if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (tbstructcol.is_identity)
 | 
			
		||||
                                    seqnameDel.Add(Utils.GetCsName($"{tbname[1]}_seq_{tbstructcol.column}"));
 | 
			
		||||
                                //修改列名
 | 
			
		||||
                                sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" RENAME COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append("';\r\n");
 | 
			
		||||
                                if (tbcol.Attribute.IsIdentity)
 | 
			
		||||
                                    seqcols.Add(NativeTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true));
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
 | 
			
		||||
                                seqcols.Add(NativeTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true));
 | 
			
		||||
                            if (isCommentChanged)
 | 
			
		||||
                                sbalter.Append("execute immediate 'COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "").Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        //添加列
 | 
			
		||||
                        sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD (").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(dbtypeNoneNotNull).Append(")';\r\n");
 | 
			
		||||
                        if (tbcol.Attribute.IsNullable == false)
 | 
			
		||||
                        {
 | 
			
		||||
                            sbalter.Append("execute immediate 'UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" = ").Append(tbcol.DbDefaultValue.Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
                            sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" NOT NULL';\r\n");
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tbcol.Attribute.IsIdentity == true) seqcols.Add(NativeTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true));
 | 
			
		||||
                        if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append("execute immediate 'COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "").Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (istmpatler == false)
 | 
			
		||||
                {
 | 
			
		||||
                    CreateOracleFunction(_orm);
 | 
			
		||||
                    var dsuksql = _commonUtils.FormatSql(@"
 | 
			
		||||
select
 | 
			
		||||
nvl(freesql_long_2_varchar(a.index_name, c.table_name, c.column_position), c.column_name),
 | 
			
		||||
a.index_name,
 | 
			
		||||
case when c.descend = 'DESC' then 1 else 0 end,
 | 
			
		||||
case when a.uniqueness = 'UNIQUE' then 1 else 0 end
 | 
			
		||||
from all_indexes a,
 | 
			
		||||
all_ind_columns c 
 | 
			
		||||
where a.index_name = c.index_name
 | 
			
		||||
and a.table_owner = c.table_owner
 | 
			
		||||
and a.table_name = c.table_name
 | 
			
		||||
and a.owner in ({0}) and a.table_name in ({1})
 | 
			
		||||
and not exists(select 1 from all_constraints where constraint_name = a.index_name and constraint_type = 'P')", tboldname ?? tbname);
 | 
			
		||||
                    var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]).Trim('"'), string.Concat(a[1]), string.Concat(a[2]), string.Concat(a[3]) }).ToArray();
 | 
			
		||||
                    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 => uk.Columns.Where(b => (a[3] == "1") == uk.IsUnique && 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("execute immediate 'DROP INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append("';\r\n");
 | 
			
		||||
                            sbalter.Append("execute immediate '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 comments from all_tab_comments where owner = {0} and table_name = {1} and table_type = 'TABLE'", tbname[0], tbname[1])));
 | 
			
		||||
                    if (dbcomment != (tb.Comment ?? ""))
 | 
			
		||||
                        sb.Append("execute immediate 'COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment).Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
 | 
			
		||||
                    sb.Append(sbalter);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                var oldpk = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@" select constraint_name from user_constraints where owner={0} and table_name={1} and constraint_type='P'", tbname))?.ToString();
 | 
			
		||||
                if (string.IsNullOrEmpty(oldpk) == false)
 | 
			
		||||
                    sb.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(_commonUtils.QuoteSqlName(oldpk)).Append("';\r\n");
 | 
			
		||||
 | 
			
		||||
                //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
 | 
			
		||||
                var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}");
 | 
			
		||||
                var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}");
 | 
			
		||||
                //创建临时表
 | 
			
		||||
                sb.Append("execute immediate 'CREATE TABLE ").Append(tmptablename).Append(" ( ");
 | 
			
		||||
                foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
 | 
			
		||||
                    if (tbcol.Attribute.IsIdentity == true) seqcols.Add(NativeTuple.Create(tbcol, tbname, true));
 | 
			
		||||
                }
 | 
			
		||||
                if (tb.Primarys.Any())
 | 
			
		||||
                {
 | 
			
		||||
                    var pkname = primaryKeyName ?? $"{tbname[0]}_{tbname[1]}_pk2";
 | 
			
		||||
                    sb.Append(" \r\n  CONSTRAINT ").Append(_commonUtils.QuoteSqlName(pkname)).Append(" 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) LOGGING \r\nNOCOMPRESS \r\nNOCACHE\r\n';\r\n");
 | 
			
		||||
                //备注
 | 
			
		||||
                foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(tbcol.Comment) == false)
 | 
			
		||||
                        sb.Append("execute immediate 'COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment).Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(tb.Comment) == false)
 | 
			
		||||
                    sb.Append("execute immediate 'COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment).Replace("'", "''")).Append("';\r\n");
 | 
			
		||||
 | 
			
		||||
                sb.Append("execute immediate '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)
 | 
			
		||||
                        {
 | 
			
		||||
                            var dbtypeNoneNotNull = Regex.Replace(tbcol.Attribute.DbType, @"(NOT\s+)?NULL", "");
 | 
			
		||||
                            insertvalue = $"cast({insertvalue} as {dbtypeNoneNotNull})";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
 | 
			
		||||
                            insertvalue = $"nvl({insertvalue},{tbcol.DbDefaultValue})";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (tbcol.Attribute.IsNullable == false)
 | 
			
		||||
                        insertvalue = tbcol.DbDefaultValue;
 | 
			
		||||
                    sb.Append(insertvalue.Replace("'", "''")).Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append("';\r\n");
 | 
			
		||||
                sb.Append("execute immediate 'DROP TABLE ").Append(tablename).Append("';\r\n");
 | 
			
		||||
                sb.Append("execute immediate 'ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append("';\r\n");
 | 
			
		||||
                //创建表的索引
 | 
			
		||||
                foreach (var uk in tb.Indexes)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append("execute immediate '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");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Dictionary<string, bool> dicDeclare = new Dictionary<string, bool>();
 | 
			
		||||
            Action<string> dropSequence = seqname =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dicDeclare.ContainsKey(seqname) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sbDeclare.Append("\r\nIS").Append(seqname).Append(" NUMBER; \r\n");
 | 
			
		||||
                    dicDeclare.Add(seqname, true);
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append("IS").Append(seqname).Append(" := 0; \r\n")
 | 
			
		||||
                    .Append(" select count(1) into IS").Append(seqname).Append(_commonUtils.FormatSql(" from user_sequences where sequence_name={0}; \r\n", seqname))
 | 
			
		||||
                    .Append("if IS").Append(seqname).Append(" > 0 then \r\n")
 | 
			
		||||
                    .Append("  execute immediate 'DROP SEQUENCE ").Append(_commonUtils.QuoteSqlName(seqname)).Append("';\r\n")
 | 
			
		||||
                    .Append("end if; \r\n");
 | 
			
		||||
            };
 | 
			
		||||
            Action<string> dropTrigger = tiggerName =>
 | 
			
		||||
            {
 | 
			
		||||
                if (dicDeclare.ContainsKey(tiggerName) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sbDeclare.Append("\r\nIS").Append(tiggerName).Append(" NUMBER; \r\n");
 | 
			
		||||
                    dicDeclare.Add(tiggerName, true);
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append("IS").Append(tiggerName).Append(" := 0; \r\n")
 | 
			
		||||
                    .Append(" select count(1) into IS").Append(tiggerName).Append(_commonUtils.FormatSql(" from user_triggers where trigger_name={0}; \r\n", tiggerName))
 | 
			
		||||
                    .Append("if IS").Append(tiggerName).Append(" > 0 then \r\n")
 | 
			
		||||
                    .Append("  execute immediate 'DROP TRIGGER ").Append(_commonUtils.QuoteSqlName(tiggerName)).Append("';\r\n")
 | 
			
		||||
                    .Append("end if; \r\n");
 | 
			
		||||
            };
 | 
			
		||||
            foreach (var seqname in seqnameDel)
 | 
			
		||||
            {
 | 
			
		||||
                dropSequence(seqname);
 | 
			
		||||
                dropTrigger(seqname + "TI");
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var seqcol in seqcols)
 | 
			
		||||
            {
 | 
			
		||||
                var tbname = seqcol.Item2;
 | 
			
		||||
                var seqname = Utils.GetCsName($"{tbname[1]}_seq_{seqcol.Item1.Attribute.Name}").ToUpper();
 | 
			
		||||
                var tiggerName = seqname + "TI";
 | 
			
		||||
                var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
 | 
			
		||||
                var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name);
 | 
			
		||||
                dropSequence(seqname);
 | 
			
		||||
                if (seqcol.Item3)
 | 
			
		||||
                {
 | 
			
		||||
                    var startWith = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_tab_columns where owner={0} and table_name={1} and column_name={2}", tbname[0], tbname[1], seqcol.Item1.Attribute.Name)) == null ? 1 :
 | 
			
		||||
                        _orm.Ado.ExecuteScalar(CommandType.Text, $" select nvl(max({colname2})+1,1) from {tbname2}");
 | 
			
		||||
                    sb.Append("execute immediate 'CREATE SEQUENCE ").Append(_commonUtils.QuoteSqlName(seqname)).Append(" start with ").Append(startWith).Append("';\r\n");
 | 
			
		||||
                    sb.Append("execute immediate 'CREATE OR REPLACE TRIGGER ").Append(_commonUtils.QuoteSqlName(tiggerName))
 | 
			
		||||
                        .Append(" \r\nbefore insert on ").Append(tbname2)
 | 
			
		||||
                        .Append(" \r\nfor each row \r\nbegin\r\nselect ").Append(_commonUtils.QuoteSqlName(seqname))
 | 
			
		||||
                        .Append(".nextval into :new.").Append(colname2).Append(" from dual;\r\nend;';\r\n");
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    dropTrigger(tiggerName);
 | 
			
		||||
            }
 | 
			
		||||
            if (sbDeclare.Length > 0) sbDeclare.Insert(0, "declare ");
 | 
			
		||||
            return sb.Length == 0 ? null : sb.Insert(0, "BEGIN \r\n").Insert(0, sbDeclare.ToString()).Append("END;").ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal static string GetOracleSqlTypeFullName(object[] row)
 | 
			
		||||
        {
 | 
			
		||||
            var a = row;
 | 
			
		||||
            var sqlType = string.Concat(a[1]).ToUpper();
 | 
			
		||||
            var data_length = long.Parse(string.Concat(a[2]));
 | 
			
		||||
            long.TryParse(string.Concat(a[3]), out var data_precision);
 | 
			
		||||
            long.TryParse(string.Concat(a[4]), out var data_scale);
 | 
			
		||||
            var char_used = string.Concat(a[5]);
 | 
			
		||||
            if (Regex.IsMatch(sqlType, @"INTERVAL DAY\(\d+\) TO SECOND\(\d+\)", RegexOptions.IgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            else if (Regex.IsMatch(sqlType, @"INTERVAL YEAR\(\d+\) TO MONTH", RegexOptions.IgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            else if (sqlType.StartsWith("TIMESTAMP", StringComparison.CurrentCultureIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            else if (sqlType.StartsWith("BLOB"))
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            else if (sqlType.StartsWith("CLOB"))
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            else if (sqlType.StartsWith("NCLOB"))
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            else if (char_used.ToLower() == "c")
 | 
			
		||||
                sqlType += sqlType.StartsWith("N") ? $"({data_length / 2})" : $"({data_length / 4} CHAR)";
 | 
			
		||||
            else if (char_used.ToLower() == "b")
 | 
			
		||||
                sqlType += $"({data_length} BYTE)";
 | 
			
		||||
            else if (sqlType.ToLower() == "float")
 | 
			
		||||
                sqlType += $"({data_precision})";
 | 
			
		||||
            else if (data_precision > 0 && data_scale > 0)
 | 
			
		||||
                sqlType += $"({data_precision},{data_scale})";
 | 
			
		||||
            else if (data_precision > 0)
 | 
			
		||||
                sqlType += $"({data_precision})";
 | 
			
		||||
            else
 | 
			
		||||
                sqlType += $"({data_length})";
 | 
			
		||||
            return sqlType;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal static void CreateOracleFunction(IFreeSql fsql)
 | 
			
		||||
        {
 | 
			
		||||
            fsql.Ado.ExecuteNonQuery(CommandType.Text, @"
 | 
			
		||||
CREATE OR REPLACE FUNCTION freesql_long_2_varchar (
 | 
			
		||||
   p_index_name        IN user_ind_expressions.index_name%TYPE,
 | 
			
		||||
   p_table_name        IN user_ind_expressions.table_name%TYPE,
 | 
			
		||||
   p_COLUMN_POSITION   IN user_ind_expressions.table_name%TYPE)
 | 
			
		||||
   RETURN VARCHAR2
 | 
			
		||||
AS
 | 
			
		||||
   l_COLUMN_EXPRESSION   LONG;
 | 
			
		||||
BEGIN
 | 
			
		||||
   SELECT COLUMN_EXPRESSION
 | 
			
		||||
     INTO l_COLUMN_EXPRESSION
 | 
			
		||||
     FROM user_ind_expressions  
 | 
			
		||||
    WHERE     index_name = p_index_name
 | 
			
		||||
          AND table_name = p_table_name
 | 
			
		||||
          AND COLUMN_POSITION = p_COLUMN_POSITION;
 | 
			
		||||
  
 | 
			
		||||
   RETURN SUBSTR (l_COLUMN_EXPRESSION, 1, 4000);
 | 
			
		||||
END;");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										584
									
								
								Providers/FreeSql.Provider.Custom/Oracle/CustomOracleDbFirst.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										584
									
								
								Providers/FreeSql.Provider.Custom/Oracle/CustomOracleDbFirst.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,584 @@
 | 
			
		||||
using FreeSql.DatabaseModel;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
    class CustomOracleDbFirst : IDbFirst
 | 
			
		||||
    {
 | 
			
		||||
        IFreeSql _orm;
 | 
			
		||||
        protected CommonUtils _commonUtils;
 | 
			
		||||
        protected CommonExpression _commonExpression;
 | 
			
		||||
        public CustomOracleDbFirst(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 dbfull = column.DbTypeTextFull?.ToLower();
 | 
			
		||||
            switch (dbfull)
 | 
			
		||||
            {
 | 
			
		||||
                case "number(1)": return DbType.Boolean;
 | 
			
		||||
 | 
			
		||||
                case "number(4)": return DbType.SByte;
 | 
			
		||||
                case "number(6)": return DbType.Int16;
 | 
			
		||||
                case "number(11)": return DbType.Int32;
 | 
			
		||||
                case "number(21)": return DbType.Int64;
 | 
			
		||||
 | 
			
		||||
                case "number(3)": return DbType.Byte;
 | 
			
		||||
                case "number(5)": return DbType.UInt16;
 | 
			
		||||
                case "number(10)": return DbType.UInt32;
 | 
			
		||||
                case "number(20)": return DbType.UInt64;
 | 
			
		||||
 | 
			
		||||
                case "float(126)": return DbType.Double;
 | 
			
		||||
                case "float(63)": return DbType.Single;
 | 
			
		||||
                case "number(10,2)": return DbType.Decimal;
 | 
			
		||||
 | 
			
		||||
                case "interval day(2) to second(6)": return DbType.Time;
 | 
			
		||||
                case "timestamp(6)": return DbType.DateTime;
 | 
			
		||||
                case "timestamp(6) with local time zone": return DbType.DateTime;
 | 
			
		||||
 | 
			
		||||
                case "blob": return DbType.Binary;
 | 
			
		||||
                case "nvarchar2(255)": return DbType.String;
 | 
			
		||||
 | 
			
		||||
                case "char(36 char)": return DbType.Guid;
 | 
			
		||||
            }
 | 
			
		||||
            switch (column.DbTypeText?.ToLower())
 | 
			
		||||
            {
 | 
			
		||||
                case "number":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["number(10,2)"]);
 | 
			
		||||
                    return DbType.Decimal;
 | 
			
		||||
                case "float":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["float(126)"]);
 | 
			
		||||
                    return DbType.Double;
 | 
			
		||||
                case "interval day to second":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["interval day(2) to second(6)"]);
 | 
			
		||||
                    return DbType.Time;
 | 
			
		||||
                case "date":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["date"]);
 | 
			
		||||
                    return DbType.DateTime;
 | 
			
		||||
                case "timestamp":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["timestamp(6)"]);
 | 
			
		||||
                    return DbType.DateTime;
 | 
			
		||||
                case "timestamp with local time zone":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["timestamp(6) with local time zone"]);
 | 
			
		||||
                    return DbType.DateTime;
 | 
			
		||||
                case "blob":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["blob"]);
 | 
			
		||||
                    return DbType.Binary;
 | 
			
		||||
                case "nvarchar2":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
                case "varchar2":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
                case "char":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
                case "nchar":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
                case "clob":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
                case "nclob":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
                case "raw":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["blob"]);
 | 
			
		||||
                    return DbType.Binary;
 | 
			
		||||
                case "long raw":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["blob"]);
 | 
			
		||||
                    return DbType.Binary;
 | 
			
		||||
                case "binary_float":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["float(63)"]);
 | 
			
		||||
                    return DbType.Single;
 | 
			
		||||
                case "binary_double":
 | 
			
		||||
                    _dicDbToCs.TryAdd(dbfull, _dicDbToCs["float(126)"]);
 | 
			
		||||
                    return DbType.Double;
 | 
			
		||||
                case "rowid":
 | 
			
		||||
                default:
 | 
			
		||||
                    if (dbfull != null) _dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
 | 
			
		||||
                    return DbType.String;
 | 
			
		||||
            }
 | 
			
		||||
            throw new NotImplementedException(CoreStrings.S_TypeMappingNotImplemented(column.DbTypeTextFull));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static ConcurrentDictionary<string, DbToCs> _dicDbToCs = new ConcurrentDictionary<string, DbToCs>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
        static CustomOracleDbFirst()
 | 
			
		||||
        {
 | 
			
		||||
            var defaultDbToCs = new Dictionary<string, DbToCs>() {
 | 
			
		||||
                { "number(1)", new DbToCs("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
 | 
			
		||||
 | 
			
		||||
                { "number(4)", new DbToCs("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { "number(6)", new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { "number(11)", new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { "number(21)", new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
 | 
			
		||||
 | 
			
		||||
                { "number(3)", new DbToCs("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
 | 
			
		||||
                { "number(5)", new DbToCs("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { "number(10)", new DbToCs("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt64") },
 | 
			
		||||
                { "number(20)", new DbToCs("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetDecimal") },
 | 
			
		||||
 | 
			
		||||
                { "float(126)", new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
 | 
			
		||||
                { "float(63)", new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
 | 
			
		||||
                { "number(10,2)", new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
 | 
			
		||||
 | 
			
		||||
                { "interval day(2) to second(6)", new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
 | 
			
		||||
                { "date", new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") },
 | 
			
		||||
                { "timestamp(6)", new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") },
 | 
			
		||||
                { "timestamp(6) with local time zone", new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") },
 | 
			
		||||
 | 
			
		||||
                { "blob", new DbToCs("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
 | 
			
		||||
 | 
			
		||||
                { "nvarchar2(255)", new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
                { "char(36 char)", new DbToCs("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}.Value", "GetGuid") },
 | 
			
		||||
            };
 | 
			
		||||
            foreach (var kv in defaultDbToCs)
 | 
			
		||||
                _dicDbToCs.TryAdd(kv.Key, kv.Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
 | 
			
		||||
        public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csParse : null;
 | 
			
		||||
        public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csStringify : null;
 | 
			
		||||
        public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
 | 
			
		||||
        public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csTypeInfo : null;
 | 
			
		||||
        public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csTypeValue : null;
 | 
			
		||||
        public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.dataReaderMethod : null;
 | 
			
		||||
 | 
			
		||||
        public List<string> GetDatabases()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = @" select username from all_users";
 | 
			
		||||
            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 userId = (_orm.Ado as CustomOracleAdo)?.UserId;
 | 
			
		||||
                if (string.IsNullOrEmpty(userId))
 | 
			
		||||
                    using (var conn = _orm.Ado.MasterPool.Get())
 | 
			
		||||
                    {
 | 
			
		||||
                        userId = CustomOracleAdo.GetUserId(conn.Value.ConnectionString);
 | 
			
		||||
                    }
 | 
			
		||||
                tbname = new[] { userId, tbname[0] };
 | 
			
		||||
            }
 | 
			
		||||
            if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
 | 
			
		||||
            var sql = $" select 1 from all_tab_comments where {(ignoreCase ? "lower(owner)" : "owner")}={_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>();
 | 
			
		||||
            var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
 | 
			
		||||
            string[] tbname = null;
 | 
			
		||||
            if (string.IsNullOrEmpty(tablename) == false)
 | 
			
		||||
            {
 | 
			
		||||
                tbname = _commonUtils.SplitTableName(tablename);
 | 
			
		||||
                if (tbname?.Length == 1)
 | 
			
		||||
                {
 | 
			
		||||
                    var userUsers = _orm.Ado.ExecuteScalar(" select username from user_users")?.ToString();
 | 
			
		||||
                    if (string.IsNullOrEmpty(userUsers)) return loc1;
 | 
			
		||||
                    tbname = new[] { userUsers, tbname[0] };
 | 
			
		||||
                }
 | 
			
		||||
                if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
 | 
			
		||||
                database = new[] { tbname[0] };
 | 
			
		||||
            }
 | 
			
		||||
            else if (database == null || database.Any() == false)
 | 
			
		||||
            {
 | 
			
		||||
                var userUsers = _orm.Ado.ExecuteScalar(" select username from user_users")?.ToString();
 | 
			
		||||
                if (string.IsNullOrEmpty(userUsers)) return loc1;
 | 
			
		||||
                database = new[] { userUsers };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var databaseIn = string.Join(",", database.Select(a => _commonUtils.FormatSql("{0}", a)));
 | 
			
		||||
            var sql = $@"
 | 
			
		||||
select
 | 
			
		||||
a.owner || '.' || a.table_name AS tbname,
 | 
			
		||||
a.owner,
 | 
			
		||||
a.table_name,
 | 
			
		||||
b.comments,
 | 
			
		||||
'TABLE' AS tp
 | 
			
		||||
from all_tables a
 | 
			
		||||
left join all_tab_comments b on b.owner = a.owner and b.table_name = a.table_name and b.table_type = 'TABLE'
 | 
			
		||||
where {(ignoreCase ? "lower(a.owner)" : "a.owner")} in ({databaseIn}){(tbname == null ? "" : $" and {(ignoreCase ? "lower(a.table_name)" : "a.table_name")}={_commonUtils.FormatSql("{0}", tbname[1])}")}
 | 
			
		||||
 | 
			
		||||
UNION ALL
 | 
			
		||||
 | 
			
		||||
select
 | 
			
		||||
a.owner || '.' || a.view_name,
 | 
			
		||||
a.owner,
 | 
			
		||||
a.view_name,
 | 
			
		||||
b.comments,
 | 
			
		||||
'VIEW' AS tp
 | 
			
		||||
from all_views a
 | 
			
		||||
left join all_tab_comments b on b.owner = a.owner and b.table_name = a.view_name and b.table_type = 'VIEW'
 | 
			
		||||
where {(ignoreCase ? "lower(a.owner)" : "a.owner")} in ({databaseIn}){(tbname == null ? "" : $" and {(ignoreCase ? "lower(a.view_name)" : "a.view_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>());
 | 
			
		||||
                switch (type)
 | 
			
		||||
                {
 | 
			
		||||
                    case DbTableType.TABLE:
 | 
			
		||||
                    case DbTableType.VIEW:
 | 
			
		||||
                        loc6_1000.Add(table.Replace("'", "''"));
 | 
			
		||||
                        if (loc6_1000.Count >= 999)
 | 
			
		||||
                        {
 | 
			
		||||
                            loc6.Add(loc6_1000.ToArray());
 | 
			
		||||
                            loc6_1000.Clear();
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case DbTableType.StoreProcedure:
 | 
			
		||||
                        loc66_1000.Add(table.Replace("'", "''"));
 | 
			
		||||
                        if (loc66_1000.Count >= 999)
 | 
			
		||||
                        {
 | 
			
		||||
                            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(")");
 | 
			
		||||
 | 
			
		||||
            _orm.Ado.ExecuteNonQuery(CommandType.Text, @"
 | 
			
		||||
CREATE OR REPLACE FUNCTION FREESQL_LONG_TO_CHAR_DEFAULT
 | 
			
		||||
(
 | 
			
		||||
  TABLE_NAME VARCHAR,
 | 
			
		||||
  COLUMN     VARCHAR2
 | 
			
		||||
)
 | 
			
		||||
  RETURN VARCHAR AS
 | 
			
		||||
  TEXT_C1 VARCHAR2(32767);
 | 
			
		||||
  SQL_CUR VARCHAR2(2000);
 | 
			
		||||
BEGIN
 | 
			
		||||
  DBMS_OUTPUT.ENABLE(BUFFER_SIZE => NULL);
 | 
			
		||||
  SQL_CUR := 'SELECT T.DATA_DEFAULT FROM USER_TAB_COLUMNS T WHERE T.TABLE_NAME = '''||TABLE_NAME||''' AND T.COLUMN_NAME='''||COLUMN||'''';
 | 
			
		||||
  DBMS_OUTPUT.PUT_LINE(SQL_CUR);
 | 
			
		||||
  EXECUTE IMMEDIATE SQL_CUR
 | 
			
		||||
    INTO TEXT_C1;
 | 
			
		||||
  TEXT_C1 := SUBSTR(TEXT_C1, 1, 4000);
 | 
			
		||||
  RETURN TEXT_C1;
 | 
			
		||||
END;");
 | 
			
		||||
 | 
			
		||||
            sql = $@"
 | 
			
		||||
select
 | 
			
		||||
a.owner || '.' || a.table_name,
 | 
			
		||||
a.column_name,
 | 
			
		||||
a.data_type,
 | 
			
		||||
a.data_length,
 | 
			
		||||
a.data_precision,
 | 
			
		||||
a.data_scale,
 | 
			
		||||
a.char_used,
 | 
			
		||||
case when a.nullable = 'N' then 0 else 1 end,
 | 
			
		||||
nvl((select 1 from user_sequences where upper(sequence_name)=upper(a.table_name||'_seq_'||a.column_name) and rownum < 2), 0),
 | 
			
		||||
to_char(b.comments),
 | 
			
		||||
nvl(FREESQL_LONG_TO_CHAR_DEFAULT(a.table_name, a.column_name),'')
 | 
			
		||||
from all_tab_cols a
 | 
			
		||||
left join all_col_comments b on b.owner = a.owner and b.table_name = a.table_name and b.column_name = a.column_name
 | 
			
		||||
where {(ignoreCase ? "lower(a.owner)" : "a.owner")} in ({databaseIn}) and {loc8} and a.column_id is not null
 | 
			
		||||
";
 | 
			
		||||
            ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
            var ds2 = new List<object[]>();
 | 
			
		||||
            foreach (var row in ds)
 | 
			
		||||
            {
 | 
			
		||||
                var ds2item = new object[9];
 | 
			
		||||
                ds2item[0] = row[0];
 | 
			
		||||
                ds2item[1] = row[1];
 | 
			
		||||
                ds2item[2] = Regex.Replace(string.Concat(row[2]), @"\(\d+\)", "");
 | 
			
		||||
                ds2item[4] = CustomOracleCodeFirst.GetOracleSqlTypeFullName(new object[] { row[1], row[2], row[3], row[4], row[5], row[6] });
 | 
			
		||||
                ds2item[5] = string.Concat(row[7]);
 | 
			
		||||
                ds2item[6] = string.Concat(row[8]);
 | 
			
		||||
                ds2item[7] = string.Concat(row[9]);
 | 
			
		||||
                ds2item[8] = string.Concat(row[10]);
 | 
			
		||||
                ds2.Add(ds2item);
 | 
			
		||||
            }
 | 
			
		||||
            var position = 0;
 | 
			
		||||
            foreach (var row in ds2)
 | 
			
		||||
            {
 | 
			
		||||
                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]);
 | 
			
		||||
                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]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CustomOracleCodeFirst.CreateOracleFunction(_orm);
 | 
			
		||||
            sql = $@"
 | 
			
		||||
select
 | 
			
		||||
a.table_owner || '.' || a.table_name,
 | 
			
		||||
nvl(freesql_long_2_varchar(a.index_name, c.table_name, c.column_position), c.column_name),
 | 
			
		||||
c.index_name,
 | 
			
		||||
case when a.uniqueness = 'UNIQUE' then 1 else 0 end,
 | 
			
		||||
case when exists(select 1 from all_constraints where constraint_name = a.index_name and constraint_type = 'P') then 1 else 0 end,
 | 
			
		||||
0,
 | 
			
		||||
case when c.descend = 'DESC' then 1 else 0 end,
 | 
			
		||||
c.column_position
 | 
			
		||||
from all_indexes a,
 | 
			
		||||
all_ind_columns c 
 | 
			
		||||
where a.index_name = c.index_name
 | 
			
		||||
and a.table_owner = c.table_owner
 | 
			
		||||
and a.table_name = c.table_name
 | 
			
		||||
and {(ignoreCase ? "lower(a.table_owner)" : "a.table_owner")} in ({databaseIn}) and {loc8}
 | 
			
		||||
";
 | 
			
		||||
            ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
            var indexColumns = new Dictionary<string, Dictionary<string, DbIndexInfo>>();
 | 
			
		||||
            var uniqueColumns = new Dictionary<string, Dictionary<string, DbIndexInfo>>();
 | 
			
		||||
            foreach (var row in ds)
 | 
			
		||||
            {
 | 
			
		||||
                string table_id = string.Concat(row[0]);
 | 
			
		||||
                string column = string.Concat(row[1]).Trim('"');
 | 
			
		||||
                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;
 | 
			
		||||
                if (is_primary_key) continue;
 | 
			
		||||
 | 
			
		||||
                Dictionary<string, DbIndexInfo> loc10 = null;
 | 
			
		||||
                DbIndexInfo loc11 = null;
 | 
			
		||||
                if (!indexColumns.TryGetValue(table_id, out loc10))
 | 
			
		||||
                    indexColumns.Add(table_id, loc10 = new Dictionary<string, DbIndexInfo>());
 | 
			
		||||
                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>());
 | 
			
		||||
                    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
 | 
			
		||||
a.owner || '.' || a.table_name,
 | 
			
		||||
c.column_name,
 | 
			
		||||
c.constraint_name,
 | 
			
		||||
b.owner || '.' || b.table_name,
 | 
			
		||||
1,
 | 
			
		||||
d.column_name
 | 
			
		||||
 | 
			
		||||
-- a.owner 外键拥有者,
 | 
			
		||||
-- a.table_name 外键表,
 | 
			
		||||
-- c.column_name 外键列,
 | 
			
		||||
-- b.owner 主键拥有者,
 | 
			
		||||
-- b.table_name 主键表,
 | 
			
		||||
-- d.column_name 主键列,
 | 
			
		||||
-- c.constraint_name 外键名,
 | 
			
		||||
-- d.constraint_name 主键名
 | 
			
		||||
 | 
			
		||||
from
 | 
			
		||||
all_constraints a,
 | 
			
		||||
all_constraints b,
 | 
			
		||||
all_cons_columns c, --外键表
 | 
			
		||||
all_cons_columns d  --主键表
 | 
			
		||||
where
 | 
			
		||||
a.r_constraint_name = b.constraint_name   
 | 
			
		||||
and a.constraint_type = 'R'   
 | 
			
		||||
and b.constraint_type = 'P'   
 | 
			
		||||
and a.r_owner = b.owner   
 | 
			
		||||
and a.constraint_name = c.constraint_name   
 | 
			
		||||
and b.constraint_name = d.constraint_name   
 | 
			
		||||
and a.owner = c.owner   
 | 
			
		||||
and a.table_name = c.table_name   
 | 
			
		||||
and b.owner = d.owner   
 | 
			
		||||
and b.table_name = d.table_name
 | 
			
		||||
and {(ignoreCase ? "lower(a.owner)" : "a.owner")} in ({databaseIn}) and {loc8}
 | 
			
		||||
";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>();
 | 
			
		||||
                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>());
 | 
			
		||||
                    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>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,569 @@
 | 
			
		||||
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.Oracle
 | 
			
		||||
{
 | 
			
		||||
    class CustomOracleExpression : CommonExpression
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public CustomOracleExpression(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 $"dbms_lob.getlength({getExp(arrOper)})";
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.Convert:
 | 
			
		||||
                    var operandExp = (exp as UnaryExpression)?.Operand;
 | 
			
		||||
                    var gentype = exp.Type.NullableTypeOrThis();
 | 
			
		||||
                    if (gentype != operandExp.Type.NullableTypeOrThis())
 | 
			
		||||
                    {
 | 
			
		||||
                        switch (exp.Type.NullableTypeOrThis().ToString())
 | 
			
		||||
                        {
 | 
			
		||||
                            //case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
 | 
			
		||||
                            case "System.Byte": return $"cast({getExp(operandExp)} as number)";
 | 
			
		||||
                            case "System.Char": return $"substr(to_char({getExp(operandExp)}), 1, 1)";
 | 
			
		||||
                            case "System.DateTime": return $"to_timestamp({getExp(operandExp)},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                            case "System.Decimal": return $"cast({getExp(operandExp)} as number)";
 | 
			
		||||
                            case "System.Double": return $"cast({getExp(operandExp)} as number)";
 | 
			
		||||
                            case "System.Int16":
 | 
			
		||||
                            case "System.Int32":
 | 
			
		||||
                            case "System.Int64":
 | 
			
		||||
                            case "System.SByte": return $"cast({getExp(operandExp)} as number)";
 | 
			
		||||
                            case "System.Single": return $"cast({getExp(operandExp)} as number)";
 | 
			
		||||
                            case "System.String": return $"to_char({getExp(operandExp)})";
 | 
			
		||||
                            case "System.UInt16":
 | 
			
		||||
                            case "System.UInt32":
 | 
			
		||||
                            case "System.UInt64": return $"cast({getExp(operandExp)} as number)";
 | 
			
		||||
                            case "System.Guid":
 | 
			
		||||
                                if (tsc.mapType == typeof(byte[])) return $"hextoraw({getExp(operandExp)})";
 | 
			
		||||
                                return $"to_char({getExp(operandExp)})";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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 number)";
 | 
			
		||||
                                case "System.Char": return $"substr(to_char({getExp(callExp.Arguments[0])}), 1, 1)";
 | 
			
		||||
                                case "System.DateTime": return $"to_timestamp({getExp(callExp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                                case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as number)";
 | 
			
		||||
                                case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as number)";
 | 
			
		||||
                                case "System.Int16":
 | 
			
		||||
                                case "System.Int32":
 | 
			
		||||
                                case "System.Int64":
 | 
			
		||||
                                case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as number)";
 | 
			
		||||
                                case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as number)";
 | 
			
		||||
                                case "System.UInt16":
 | 
			
		||||
                                case "System.UInt32":
 | 
			
		||||
                                case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as number)";
 | 
			
		||||
                                case "System.Guid":
 | 
			
		||||
                                    if (tsc.mapType == typeof(byte[])) return $"hextoraw({getExp(callExp.Arguments[0])})";
 | 
			
		||||
                                    return $"to_char({getExp(callExp.Arguments[0])})";
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "NewGuid":
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "Next":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "cast(dbms_random.value*1000000000 as smallint)";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "NextDouble":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "dbms_random.value";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "Random":
 | 
			
		||||
                            if (callExp.Method.DeclaringType.IsNumberType()) return "dbms_random.value";
 | 
			
		||||
                            return null;
 | 
			
		||||
                        case "ToString":
 | 
			
		||||
                            if (callExp.Object != null) return callExp.Arguments.Count == 0 ? $"to_char({getExp(callExp.Object)})" : 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 $"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 "trunc(systimestamp)";
 | 
			
		||||
                    case "MinValue": return "to_timestamp('0001-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                    case "MaxValue": return "to_timestamp('9999-12-31 23:59:59','YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Date": return $"trunc({left})";
 | 
			
		||||
                case "TimeOfDay": return $"({left}-trunc({left}))";
 | 
			
		||||
                case "DayOfWeek": return $"case when to_char({left},'D')='7' then 0 else cast(to_char({left},'D') as number) end";
 | 
			
		||||
                case "Day": return $"cast(to_char({left},'DD') as number)";
 | 
			
		||||
                case "DayOfYear": return $"cast(to_char({left},'DDD') as number)";
 | 
			
		||||
                case "Month": return $"cast(to_char({left},'MM') as number)";
 | 
			
		||||
                case "Year": return $"cast(to_char({left},'YYYY') as number)";
 | 
			
		||||
                case "Hour": return $"cast(to_char({left},'HH24') as number)";
 | 
			
		||||
                case "Minute": return $"cast(to_char({left},'MI') as number)";
 | 
			
		||||
                case "Second": return $"cast(to_char({left},'SS') as number)";
 | 
			
		||||
                case "Millisecond": return $"cast(to_char({left},'FF3') as number)";
 | 
			
		||||
                case "Ticks": return $"cast(to_char({left},'FF7') as number)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Zero": return "numtodsinterval(0,'second')";
 | 
			
		||||
                    case "MinValue": return "numtodsinterval(-233720368.5477580,'second')";
 | 
			
		||||
                    case "MaxValue": return "numtodsinterval(233720368.5477580,'second')";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Days": return $"extract(day from {left})";
 | 
			
		||||
                case "Hours": return $"extract(hour from {left})";
 | 
			
		||||
                case "Milliseconds": return $"cast(substr(extract(second from {left})-floor(extract(second from {left})),3,3) as number)";
 | 
			
		||||
                case "Minutes": return $"extract(minute from {left})";
 | 
			
		||||
                case "Seconds": return $"floor(extract(second from {left}))";
 | 
			
		||||
                case "Ticks": return $"(extract(day from {left})*86400+extract(hour from {left})*3600+extract(minute from {left})*60+extract(second from {left}))*10000000";
 | 
			
		||||
                case "TotalDays": return $"extract(day from {left})";
 | 
			
		||||
                case "TotalHours": return $"(extract(day from {left})*24+extract(hour from {left}))";
 | 
			
		||||
                case "TotalMilliseconds": return $"(extract(day from {left})*86400+extract(hour from {left})*3600+extract(minute from {left})*60+extract(second from {left}))*1000";
 | 
			
		||||
                case "TotalMinutes": return $"(extract(day from {left})*1440+extract(hour from {left})*60+extract(minute from {left}))";
 | 
			
		||||
                case "TotalSeconds": return $"(extract(day from {left})*86400+extract(hour from {left})*3600+extract(minute from {left})*60+extract(second from {left}))";
 | 
			
		||||
            }
 | 
			
		||||
            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]));
 | 
			
		||||
                        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 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("StringJoinOracleGroupConcat"),
 | 
			
		||||
                                        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 $"instr({args0Value}, {left}) = 1";
 | 
			
		||||
                            if (exp.Method.Name == "EndsWith") return $"instr({args0Value}, {left}) = length({args0Value})";
 | 
			
		||||
                            return $"instr({args0Value}, {left}) > 0";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(to_char({args0Value})||'%')")}";
 | 
			
		||||
                        if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%'||to_char({args0Value}))")}";
 | 
			
		||||
                        if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
 | 
			
		||||
                        return $"({left}) LIKE ('%'||to_char({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 $"(instr({left}, {indexOfFindStr}, {locateArgs1}, 1)-1)";
 | 
			
		||||
                        }
 | 
			
		||||
                        return $"(instr({left}, {indexOfFindStr}, 1, 1))-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(both {getExp(argsTrim01)} from {left})";
 | 
			
		||||
                                if (exp.Method.Name == "TrimStart") left = $"ltrim({left},{getExp(argsTrim01)})";
 | 
			
		||||
                                if (exp.Method.Name == "TrimEnd") left = $"rtrim({left},{getExp(argsTrim01)})";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return left;
 | 
			
		||||
                    case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end";
 | 
			
		||||
                    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 $"ceil({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":
 | 
			
		||||
                    if (exp.Arguments.Count > 1) return $"log({getExp(exp.Arguments[1])},{getExp(exp.Arguments[0])})";
 | 
			
		||||
                    return $"log(2.7182818284590451,{getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log10": return $"log(10,{getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Pow": return $"power({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 $"trunc({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 $"extract(day from ({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])})))";
 | 
			
		||||
                    case "DaysInMonth": return $"cast(to_char(last_day(to_date(({getExp(exp.Arguments[0])})||'-'||({getExp(exp.Arguments[1])})||'-01','yyyy-mm-dd')),'DD') as number)";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
 | 
			
		||||
                    case "IsLeapYear":
 | 
			
		||||
                        var isLeapYearArgs1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"(mod({isLeapYearArgs1},4)=0 AND mod({isLeapYearArgs1},100)<>0 OR mod({isLeapYearArgs1},400)=0)";
 | 
			
		||||
 | 
			
		||||
                    case "Parse": return $"to_timestamp({getExp(exp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return $"to_timestamp({getExp(exp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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 "AddDays": return $"({left}+{args1})";
 | 
			
		||||
                    case "AddHours": return $"({left}+({args1})/24)";
 | 
			
		||||
                    case "AddMilliseconds": return $"({left}+({args1})/86400000)";
 | 
			
		||||
                    case "AddMinutes": return $"({left}+({args1})/1440)";
 | 
			
		||||
                    case "AddMonths": return $"add_months({left},{args1})";
 | 
			
		||||
                    case "AddSeconds": return $"({left}+({args1})/86400)";
 | 
			
		||||
                    case "AddTicks": return $"({left}+({args1})/864000000000)";
 | 
			
		||||
                    case "AddYears": return $"add_months({left},({args1})*12)";
 | 
			
		||||
                    case "Subtract":
 | 
			
		||||
                        switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.DateTime": return $"numtodsinterval(({left}+0)-({args1}+0),'day')";
 | 
			
		||||
                            case "System.TimeSpan": return $"({left}-{args1})";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"extract(day from ({left}-({args1})))";
 | 
			
		||||
                    case "ToString":
 | 
			
		||||
                        if (left.StartsWith("'") || left.EndsWith("'")) left = $"to_timestamp({left},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                        if (exp.Arguments.Count == 0) return $"to_char({left},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                        switch (args1)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "'yyyy-MM-dd HH:mm:ss'": return $"to_char({left},'YYYY-MM-DD HH24:MI:SS')";
 | 
			
		||||
                            case "'yyyy-MM-dd HH:mm'": return $"to_char({left},'YYYY-MM-DD HH24:MI')";
 | 
			
		||||
                            case "'yyyy-MM-dd HH'": return $"to_char({left},'YYYY-MM-DD HH24')";
 | 
			
		||||
                            case "'yyyy-MM-dd'": return $"to_char({left},'YYYY-MM-DD')";
 | 
			
		||||
                            case "'yyyy-MM'": return $"to_char({left},'YYYY-MM')";
 | 
			
		||||
                            case "'yyyyMMddHHmmss'": return $"to_char({left},'YYYYMMDDHH24MISS')";
 | 
			
		||||
                            case "'yyyyMMddHHmm'": return $"to_char({left},'YYYYMMDDHH24MI')";
 | 
			
		||||
                            case "'yyyyMMddHH'": return $"to_char({left},'YYYYMMDDHH24')";
 | 
			
		||||
                            case "'yyyyMMdd'": return $"to_char({left},'YYYYMMDD')";
 | 
			
		||||
                            case "'yyyyMM'": return $"to_char({left},'YYYYMM')";
 | 
			
		||||
                            case "'yyyy'": return $"to_char({left},'YYYY')";
 | 
			
		||||
                            case "'HH:mm:ss'": return $"to_char({left},'HH24:MI:SS')";
 | 
			
		||||
                        }
 | 
			
		||||
                        args1 = Regex.Replace(args1, "(yyyy|yy|MM|dd|HH|hh|mm|ss|tt)", m =>
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (m.Groups[1].Value)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "yyyy": return $"YYYY";
 | 
			
		||||
                                case "yy": return $"YY";
 | 
			
		||||
                                case "MM": return $"%_a1";
 | 
			
		||||
                                case "dd": return $"%_a2";
 | 
			
		||||
                                case "HH": return $"%_a3";
 | 
			
		||||
                                case "hh": return $"%_a4";
 | 
			
		||||
                                case "mm": return $"%_a5";
 | 
			
		||||
                                case "ss": return $"SS";
 | 
			
		||||
                                case "tt": return $"%_a6";
 | 
			
		||||
                            }
 | 
			
		||||
                            return m.Groups[0].Value;
 | 
			
		||||
                        });
 | 
			
		||||
                        var argsFinds = new[] { "YYYY", "YY", "%_a1", "%_a2", "%_a3", "%_a4", "%_a5", "SS", "%_a6" };
 | 
			
		||||
                        var argsSpts = Regex.Split(args1, "(M|d|H|h|m|s|t)");
 | 
			
		||||
                        for (var a = 0; a < argsSpts.Length; a++)
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (argsSpts[a])
 | 
			
		||||
                            {
 | 
			
		||||
                                case "M": argsSpts[a] = $"ltrim(to_char({left},'MM'),'0')"; break;
 | 
			
		||||
                                case "d": argsSpts[a] = $"case when substr(to_char({left},'DD'),1,1) = '0' then substr(to_char({left},'DD'),2,1) else to_char({left},'DD') end"; break;
 | 
			
		||||
                                case "H": argsSpts[a] = $"case when substr(to_char({left},'HH24'),1,1) = '0' then substr(to_char({left},'HH24'),2,1) else to_char({left},'HH24') end"; break;
 | 
			
		||||
                                case "h": argsSpts[a] = $"case when substr(to_char({left},'HH12'),1,1) = '0' then substr(to_char({left},'HH12'),2,1) else to_char({left},'HH12') end"; break;
 | 
			
		||||
                                case "m": argsSpts[a] = $"case when substr(to_char({left},'MI'),1,1) = '0' then substr(to_char({left},'MI'),2,1) else to_char({left},'MI') end"; break;
 | 
			
		||||
                                case "s": argsSpts[a] = $"case when substr(to_char({left},'SS'),1,1) = '0' then substr(to_char({left},'SS'),2,1) else to_char({left},'SS') end"; break;
 | 
			
		||||
                                case "t": argsSpts[a] = $"rtrim(to_char({left},'AM'),'M')"; 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)) ? $"to_char({left},'{argsSptsA}')" : $"'{argsSptsA}'"; 
 | 
			
		||||
                                    break;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (argsSpts.Length > 0) args1 = $"({string.Join(" || ", argsSpts.Where(a => a != "''"))})";
 | 
			
		||||
                        return args1.Replace("%_a1", "MM").Replace("%_a2", "DD").Replace("%_a3", "HH24").Replace("%_a4", "HH12").Replace("%_a5", "MI").Replace("%_a6", "AM");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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 $"extract(day from ({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])})))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "FromDays": return $"numtodsinterval(({getExp(exp.Arguments[0])})*{(long)60 * 60 * 24},'second')";
 | 
			
		||||
                    case "FromHours": return $"numtodsinterval(({getExp(exp.Arguments[0])})*{(long)60 * 60},'second')";
 | 
			
		||||
                    case "FromMilliseconds": return $"numtodsinterval(({getExp(exp.Arguments[0])})/1000,'second')";
 | 
			
		||||
                    case "FromMinutes": return $"numtodsinterval(({getExp(exp.Arguments[0])})*60,'second')";
 | 
			
		||||
                    case "FromSeconds": return $"numtodsinterval(({getExp(exp.Arguments[0])}),'second')";
 | 
			
		||||
                    case "FromTicks": return $"numtodsinterval(({getExp(exp.Arguments[0])})/10000000,'second')";
 | 
			
		||||
                    case "Parse": return $"cast({getExp(exp.Arguments[0])} as interval day(9) to second(7))";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as interval day(9) to second(7))";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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 $"extract(day from ({left}-({args1})))";
 | 
			
		||||
                    case "ToString": return $"to_char({left})";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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 number)";
 | 
			
		||||
                    case "ToChar": return $"substr(to_char({getExp(exp.Arguments[0])}), 1, 1)";
 | 
			
		||||
                    case "ToDateTime": return $"to_timestamp({getExp(exp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
 | 
			
		||||
                    case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as number)";
 | 
			
		||||
                    case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as number)";
 | 
			
		||||
                    case "ToInt16":
 | 
			
		||||
                    case "ToInt32":
 | 
			
		||||
                    case "ToInt64":
 | 
			
		||||
                    case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as number)";
 | 
			
		||||
                    case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as number)";
 | 
			
		||||
                    case "ToString": return $"to_char({getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "ToUInt16":
 | 
			
		||||
                    case "ToUInt32":
 | 
			
		||||
                    case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as number)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,51 @@
 | 
			
		||||
using FreeSql.Internal.CommonProvider;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Custom.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class CustomOracleProvider<TMark> : BaseDbProvider, IFreeSql<TMark>
 | 
			
		||||
    {
 | 
			
		||||
        public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) => new CustomOracleSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IInsert<T1> CreateInsertProvider<T1>() => new CustomOracleInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere) => new CustomOracleUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere) => new CustomOracleDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>() => new CustomOracleInsertOrUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
 | 
			
		||||
        public CustomOracleProvider(string masterConnectionString, string[] slaveConnectionString, Func<DbConnection> connectionFactory = null)
 | 
			
		||||
        {
 | 
			
		||||
            this.InternalCommonUtils = new CustomOracleUtils(this);
 | 
			
		||||
            this.InternalCommonExpression = new CustomOracleExpression(this.InternalCommonUtils);
 | 
			
		||||
 | 
			
		||||
            this.Ado = new CustomOracleAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory);
 | 
			
		||||
            this.Aop = new AopProvider();
 | 
			
		||||
 | 
			
		||||
            this.DbFirst = new CustomOracleDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
            this.CodeFirst = new CustomOracleCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
 | 
			
		||||
            //this.Aop.AuditValue += new EventHandler<Aop.AuditValueEventArgs>((_, e) =>
 | 
			
		||||
            //{
 | 
			
		||||
            //    if (e.Value == null && e.Column.Attribute.IsPrimary == false && e.Column.Attribute.IsIdentity == false)
 | 
			
		||||
            //        e.Value = Utils.GetDataReaderValue(e.Property.PropertyType.NullableTypeOrThis(), e.Column.Attribute.DbDefautValue);
 | 
			
		||||
            //});
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ~CustomOracleProvider() => 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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								Providers/FreeSql.Provider.Custom/Oracle/CustomOracleUtils.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								Providers/FreeSql.Provider.Custom/Oracle/CustomOracleUtils.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
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.Oracle
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class CustomOracleUtils : CommonUtils
 | 
			
		||||
    {
 | 
			
		||||
        DbProviderFactory Factory => FreeSqlCustomAdapterGlobalExtensions.GetDbProviderFactory(_orm);
 | 
			
		||||
 | 
			
		||||
        public CustomOracleUtils(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 dbtype = (DbType?)_orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
            switch (dbtype)
 | 
			
		||||
            {
 | 
			
		||||
                case DbType.Boolean:
 | 
			
		||||
                    if (value == null) value = null;
 | 
			
		||||
                    else value = (bool)value == true ? 1 : 0;
 | 
			
		||||
                    dbtype = DbType.Int32;
 | 
			
		||||
                    break;
 | 
			
		||||
               
 | 
			
		||||
                case DbType.String:
 | 
			
		||||
                case DbType.AnsiString:
 | 
			
		||||
                case DbType.StringFixedLength:
 | 
			
		||||
                case DbType.AnsiStringFixedLength:
 | 
			
		||||
                    value = string.Concat(value);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            var ret = Factory.CreateParameter();
 | 
			
		||||
            ret.ParameterName = QuoteParamterName(parameterName);
 | 
			
		||||
            if (dbtype != null) ret.DbType = dbtype.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 dbtype = (DbType?)_orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
                switch (dbtype)
 | 
			
		||||
                {
 | 
			
		||||
                    case DbType.Boolean:
 | 
			
		||||
                        if (value == null) value = null;
 | 
			
		||||
                        else value = (bool)value == true ? 1 : 0;
 | 
			
		||||
                        dbtype = DbType.Int32;
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case DbType.String:
 | 
			
		||||
                    case DbType.AnsiString:
 | 
			
		||||
                    case DbType.StringFixedLength:
 | 
			
		||||
                    case DbType.AnsiStringFixedLength:
 | 
			
		||||
                        value = string.Concat(value);
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
                var ret = Factory.CreateParameter();
 | 
			
		||||
                ret.ParameterName = $":{name}";
 | 
			
		||||
                if (dbtype != null) ret.DbType = dbtype.Value;
 | 
			
		||||
                ret.Value = value;
 | 
			
		||||
                return ret;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomOracle(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) => $"nvl({sql}, {value})";
 | 
			
		||||
        public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}";
 | 
			
		||||
        public override string Mod(string left, string right, Type leftType, Type rightType) => $"mod({left}, {right})";
 | 
			
		||||
        public override string Div(string left, string right, Type leftType, Type rightType) => $"trunc({left} / {right})";
 | 
			
		||||
        public override string Now => "systimestamp";
 | 
			
		||||
        public override string NowUtc => "sys_extract_utc(systimestamp)";
 | 
			
		||||
 | 
			
		||||
        public override string QuoteWriteParamterAdapter(Type type, string paramterName) => paramterName;
 | 
			
		||||
        protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName) => 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(string))
 | 
			
		||||
            {
 | 
			
		||||
                var valueString = value as string;
 | 
			
		||||
                if (valueString != null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (valueString.Length < 4000) return string.Concat("'", valueString.Replace("'", "''"), "'");
 | 
			
		||||
                    var pam = AppendParamter(specialParams, $"p_{specialParams?.Count}{specialParamFlag}", col, type, value);
 | 
			
		||||
                    return pam.ParameterName;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (type == typeof(byte[]))
 | 
			
		||||
            {
 | 
			
		||||
                var valueBytes = value as byte[];
 | 
			
		||||
                if (valueBytes != null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (valueBytes.Length < 4000) return $"hextoraw('{CommonUtils.BytesSqlRaw(valueBytes)}')";
 | 
			
		||||
                    var pam = AppendParamter(specialParams, $"p_{specialParams?.Count}{specialParamFlag}", col, type, value);
 | 
			
		||||
                    return pam.ParameterName;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return FormatSql("{0}", value, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user