mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	## v0.9.18
- 增加 PostgreSQL 的 Odbc 访问提供,相比 FreeSql.Provider.PostgreSQL 支持的类型更少; - 增加 通用的 Odbc 访问提供,不能迁移实体到数据库,不能 Skip 这样来分页,理论上能 crud 所有 odbc 数据库;
This commit is contained in:
		
							
								
								
									
										21
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcDelete.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcDelete.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override List<T1> ExecuteDeleted() => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
        public override Task<List<T1>> ExecuteDeletedAsync() => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										107
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcInsert.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcInsert.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using SafeObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        OdbcUtils _utils;
 | 
			
		||||
        public OdbcInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
            _utils = _commonUtils as OdbcUtils;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override long ExecuteIdentity() => base.SplitExecuteIdentity(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
 | 
			
		||||
        protected override long RawExecuteIdentity()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            Object<DbConnection> poolConn = null;
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, string.Concat(sql, $"; {_utils.Adapter.InsertAfterGetIdentitySql}"), _params);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var conn = _connection;
 | 
			
		||||
                if (_transaction != null) conn = _transaction.Connection;
 | 
			
		||||
                if (conn == null)
 | 
			
		||||
                {
 | 
			
		||||
                    poolConn = _orm.Ado.MasterPool.Get();
 | 
			
		||||
                    conn = poolConn.Value;
 | 
			
		||||
                }
 | 
			
		||||
                _orm.Ado.ExecuteNonQuery(conn, _transaction, CommandType.Text, sql, _params);
 | 
			
		||||
                ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(conn, _transaction, CommandType.Text, _utils.Adapter.InsertAfterGetIdentitySql)), out var trylng) ? trylng : 0;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                if (poolConn != null)
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(poolConn);
 | 
			
		||||
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async protected override Task<long> RawExecuteIdentityAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            Object<DbConnection> poolConn = null;
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, string.Concat(sql, $"; {_utils.Adapter.InsertAfterGetIdentitySql}"), _params);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var conn = _connection;
 | 
			
		||||
                if (_transaction != null) conn = _transaction.Connection;
 | 
			
		||||
                if (conn == null)
 | 
			
		||||
                {
 | 
			
		||||
                    poolConn = _orm.Ado.MasterPool.Get();
 | 
			
		||||
                    conn = poolConn.Value;
 | 
			
		||||
                }
 | 
			
		||||
                await _orm.Ado.ExecuteNonQueryAsync(conn, _transaction, CommandType.Text, sql, _params);
 | 
			
		||||
                ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(conn, _transaction, CommandType.Text, _utils.Adapter.InsertAfterGetIdentitySql)), out var trylng) ? trylng : 0;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                if (poolConn != null)
 | 
			
		||||
                    _orm.Ado.MasterPool.Return(poolConn);
 | 
			
		||||
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteInserted() => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
        protected override Task<List<T1>> RawExecuteInsertedAsync() => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcSelect.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcSelect.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
			
		||||
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.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        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, List<LambdaExpression> _whereCascadeExpression, IFreeSql _orm)
 | 
			
		||||
        {
 | 
			
		||||
            var _utils = _commonUtils as OdbcUtils;
 | 
			
		||||
            if (_orm.CodeFirst.IsAutoSyncStructure)
 | 
			
		||||
                _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
 | 
			
		||||
 | 
			
		||||
            if (_whereCascadeExpression.Any())
 | 
			
		||||
                foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
 | 
			
		||||
                    tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereCascadeExpression);
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var tbUnionsGt0 = tbUnions.Count > 1;
 | 
			
		||||
            for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                if (tbUnionsIdx > 0) sb.Append(" \r\n\r\nUNION ALL\r\n\r\n");
 | 
			
		||||
                if (tbUnionsGt0) sb.Append("select * from (");
 | 
			
		||||
                var tbUnion = tbUnions[tbUnionsIdx];
 | 
			
		||||
 | 
			
		||||
                var sbnav = new StringBuilder();
 | 
			
		||||
                sb.Append(_select);
 | 
			
		||||
                if (_distinct) sb.Append("DISTINCT ");
 | 
			
		||||
                if (_limit > 0 && _utils.Adapter.SelectTopStyle == OdbcAdapter.SelecTopStyle.Top) sb.Append("TOP ").Append(_skip + _limit).Append(" ");
 | 
			
		||||
                sb.Append(field);
 | 
			
		||||
                if (_skip > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(_orderby))
 | 
			
		||||
                    {
 | 
			
		||||
                        var pktb = _tables.Where(a => a.Table.Primarys.Any()).FirstOrDefault();
 | 
			
		||||
                        if (pktb != null) _orderby = string.Concat(" \r\nORDER BY ", pktb.Alias, ".", _commonUtils.QuoteSqlName(pktb?.Table.Primarys.First().Attribute.Name));
 | 
			
		||||
                        else _orderby = string.Concat(" \r\nORDER BY ", _tables.First().Alias, ".", _commonUtils.QuoteSqlName(_tables.First().Table.Columns.First().Value.Attribute.Name));
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(", ROW_NUMBER() OVER(").Append(_orderby).Append(") AS __rownum__");
 | 
			
		||||
 | 
			
		||||
                    throw new NotImplementedException("FreeSql.Odbc.Default 未实现 Skip/Offset 功能,如果需要分页请使用判断上一次 id");
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append(" \r\nFROM ");
 | 
			
		||||
                var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
 | 
			
		||||
                var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
 | 
			
		||||
                for (var a = 0; a < tbsfrom.Length; a++)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(tbsfrom[a].Alias);
 | 
			
		||||
                    if (tbsjoin.Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
 | 
			
		||||
                        for (var b = 1; b < tbsfrom.Length; b++)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(tbsfrom[b].Alias);
 | 
			
		||||
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false) sb.Append(" AND (").Append(tbsfrom[b].Cascade).Append(")");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        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).Append(")");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (a < tbsfrom.Length - 1) sb.Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var tb in tbsjoin)
 | 
			
		||||
                {
 | 
			
		||||
                    if (tb.Type == SelectTableInfoType.Parent) continue;
 | 
			
		||||
                    switch (tb.Type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case SelectTableInfoType.LeftJoin:
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.InnerJoin:
 | 
			
		||||
                            sb.Append(" \r\nINNER JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.RightJoin:
 | 
			
		||||
                            sb.Append(" \r\nRIGHT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND (").Append(tb.Cascade).Append(")");
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (_join.Length > 0) sb.Append(_join);
 | 
			
		||||
 | 
			
		||||
                sbnav.Append(_where);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_tables[0].Cascade))
 | 
			
		||||
                    sbnav.Append(" AND (").Append(_tables[0].Cascade).Append(")");
 | 
			
		||||
 | 
			
		||||
                foreach (var tb in _tables)
 | 
			
		||||
                {
 | 
			
		||||
                    if (tb.Type == SelectTableInfoType.Parent) continue;
 | 
			
		||||
                    if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
 | 
			
		||||
                        sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (sbnav.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(_groupby) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_groupby);
 | 
			
		||||
                    if (string.IsNullOrEmpty(_having) == false)
 | 
			
		||||
                        sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
 | 
			
		||||
                }
 | 
			
		||||
                if (_skip <= 0)
 | 
			
		||||
                    sb.Append(_orderby);
 | 
			
		||||
                else
 | 
			
		||||
                    sb.Insert(0, "WITH t AS ( ").Append(" ) SELECT t.* FROM t where __rownum__ > ").Append(_skip);
 | 
			
		||||
 | 
			
		||||
                sbnav.Clear();
 | 
			
		||||
                if (_limit > 0 && _utils.Adapter.SelectTopStyle == OdbcAdapter.SelecTopStyle.Limit) sb.Append(" \r\nLIMIT ").Append(_skip + _limit);
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(") ftb");
 | 
			
		||||
            }
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public OdbcSelect(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 OdbcSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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 OdbcSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); OdbcSelect<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(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class 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 OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcSelect<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 T1 : class 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 OdbcSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcUpdate.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								Providers/FreeSql.Provider.Odbc/Default/Curd/OdbcUpdate.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
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.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        OdbcUtils _utils;
 | 
			
		||||
        public OdbcUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
            _utils = _commonUtils as OdbcUtils;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
        public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(_utils.Adapter.InsertBatchSplitLimit, 255);
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteUpdated() => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
        protected override Task<List<T1>> RawExecuteUpdatedAsync() => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
 | 
			
		||||
        {
 | 
			
		||||
            if (_table.Primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append("(");
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in _table.Primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) caseWhen.Append(", ");
 | 
			
		||||
                caseWhen.Append(_utils.Adapter.CastSql(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name)), _utils.Adapter.MappingOdbcTypeVarChar));
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append(")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
 | 
			
		||||
        {
 | 
			
		||||
            if (_table.Primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in _table.Primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_utils.Adapter.CastSql(_commonUtils.FormatSql("{0}", pk.GetMapValue(d)), _utils.Adapter.MappingOdbcTypeVarChar));
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										173
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcAdapter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcAdapter.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
    public class OdbcAdapter
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Select TOP 1,或 Limit 1 风格
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public virtual SelecTopStyle SelectTopStyle => SelecTopStyle.Top;
 | 
			
		||||
        public enum SelecTopStyle { Top, Limit }
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 插入成功后,获取自增值
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public virtual string InsertAfterGetIdentitySql => "SELECT SCOPE_IDENTITY()";
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 批量插入时,自动拆分的每次执行数量
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public virtual int InsertBatchSplitLimit => 255;
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 批量更新时,自动拆分的每次执行数量
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public virtual int UpdateBatchSplitLimit => 255;
 | 
			
		||||
 | 
			
		||||
        public virtual string MappingOdbcTypeBit => "int";
 | 
			
		||||
        public virtual string MappingOdbcTypeSmallInt => "smallint";
 | 
			
		||||
        public virtual string MappingOdbcTypeInt => "int";
 | 
			
		||||
        public virtual string MappingOdbcTypeBigInt => "bigint";
 | 
			
		||||
        public virtual string MappingOdbcTypeTinyInt => "tinyint";
 | 
			
		||||
        public virtual string MappingOdbcTypeDecimal => "decimal";
 | 
			
		||||
        public virtual string MappingOdbcTypeDouble => "float";
 | 
			
		||||
        public virtual string MappingOdbcTypeReal => "real";
 | 
			
		||||
        public virtual string MappingOdbcTypeDateTime => "datetime";
 | 
			
		||||
        public virtual string MappingOdbcTypeVarBinary => "varbinary";
 | 
			
		||||
        public virtual string MappingOdbcTypeVarChar => "nvarchar";
 | 
			
		||||
        public virtual string MappingOdbcTypeText => "nvarchar(max)";
 | 
			
		||||
        public virtual string MappingOdbcTypeUniqueIdentifier => "uniqueidentifier";
 | 
			
		||||
 | 
			
		||||
        public virtual char QuoteSqlNameLeft => '[';
 | 
			
		||||
        public virtual char QuoteSqlNameRight => ']';
 | 
			
		||||
 | 
			
		||||
        public virtual string FieldSql(Type type, string columnName) => columnName;
 | 
			
		||||
        public virtual string UnicodeStringRawSql(object value) => value == null ? "NULL" : string.Concat("N'", value.ToString().Replace("'", "''"), "'");
 | 
			
		||||
        public virtual string DateTimeRawSql(object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (value == null) return "NULL";
 | 
			
		||||
            if (value.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
 | 
			
		||||
            return string.Concat("'", ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"), "'");
 | 
			
		||||
        }
 | 
			
		||||
        public virtual string TimeSpanRawSql(object value) => value == null ? "NULL" : ((TimeSpan)value).TotalSeconds.ToString();
 | 
			
		||||
        public virtual string ByteRawSql(object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (value == null) return "NULL";
 | 
			
		||||
            var bytes = value as byte[];
 | 
			
		||||
            var sb = new StringBuilder().Append("0x");
 | 
			
		||||
            foreach (var vc in bytes)
 | 
			
		||||
            {
 | 
			
		||||
                if (vc < 10) sb.Append("0");
 | 
			
		||||
                sb.Append(vc.ToString("X"));
 | 
			
		||||
            }
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual string CastSql(string sql, string to) => $"cast({sql} as {to})";
 | 
			
		||||
        public virtual string IsNullSql(string sql, object value) => $"isnull({sql}, {value})";
 | 
			
		||||
        public virtual string ConcatSql(string[] objs, Type[] types)
 | 
			
		||||
        {
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var news = new string[objs.Length];
 | 
			
		||||
            for (var a = 0; a < objs.Length; a++)
 | 
			
		||||
                news[a] = types[a] == typeof(string) ? objs[a] : $"cast({objs[a]} as nvarchar)";
 | 
			
		||||
            return string.Join(" + ", news);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
 | 
			
		||||
        public virtual string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaConvert_ToBoolean(Type type, string operand) => $"(cast({operand} as varchar) not in ('0','false'))";
 | 
			
		||||
        public virtual string LambdaConvert_ToByte(Type type, string operand) => $"cast({operand} as tinyint)";
 | 
			
		||||
        public virtual string LambdaConvert_ToChar(Type type, string operand) => $"substring(cast({operand} as varchar),1,1)";
 | 
			
		||||
        public virtual string LambdaConvert_ToDateTime(Type type, string operand) => $"cast({operand} as datetime)";
 | 
			
		||||
        public virtual string LambdaConvert_ToDecimal(Type type, string operand) => $"cast({operand} as decimal(36,18))";
 | 
			
		||||
        public virtual string LambdaConvert_ToDouble(Type type, string operand) => $"cast({operand} as decimal(32,16))";
 | 
			
		||||
        public virtual string LambdaConvert_ToInt16(Type type, string operand) => $"cast({operand} as smallint)";
 | 
			
		||||
        public virtual string LambdaConvert_ToInt32(Type type, string operand) => $"cast({operand} as int)";
 | 
			
		||||
        public virtual string LambdaConvert_ToInt64(Type type, string operand) => $"cast({operand} as bigint)";
 | 
			
		||||
        public virtual string LambdaConvert_ToSByte(Type type, string operand) => $"cast({operand} as tinyint)";
 | 
			
		||||
        public virtual string LambdaConvert_ToSingle(Type type, string operand) => $"cast({operand} as decimal(14,7))";
 | 
			
		||||
        public virtual string LambdaConvert_ToString(Type type, string operand) => type.NullableTypeOrThis() == typeof(Guid) ? $"cast({operand} as varchar(36))" : $"cast({operand} as nvarchar)";
 | 
			
		||||
        public virtual string LambdaConvert_ToUInt16(Type type, string operand) => $"cast({operand} as smallint)";
 | 
			
		||||
        public virtual string LambdaConvert_ToUInt32(Type type, string operand) => $"cast({operand} as int)";
 | 
			
		||||
        public virtual string LambdaConvert_ToUInt64(Type type, string operand) => $"cast({operand} as bigint)";
 | 
			
		||||
        public virtual string LambdaConvert_ToGuid(Type type, string operand) => $"cast({operand} as uniqueidentifier)";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaGuid_NewGuid => "newid()";
 | 
			
		||||
        public virtual string LambdaRandom_Next => "cast(rand()*1000000000 as int)";
 | 
			
		||||
        public virtual string LambdaRandom_NextDouble => "rand()";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaString_IsNullOrEmpty(string operand) => $"({operand} is null or {operand} = '')";
 | 
			
		||||
        public virtual string LambdaString_IsNullOrWhiteSpace(string operand) => $"({operand} is null or {operand} = '' or ltrim({operand}) = '')";
 | 
			
		||||
        public virtual string LambdaString_Length(string operand) => $"len({operand})";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaString_ToLower(string operand) => $"lower({operand})";
 | 
			
		||||
        public virtual string LambdaString_ToUpper(string operand) => $"upper({operand})";
 | 
			
		||||
        public virtual string LambdaString_Substring(string operand, string startIndex, string length) => string.IsNullOrEmpty(length) ? $"left({operand}, {startIndex})" : $"substring({operand}, {startIndex}, {length})";
 | 
			
		||||
        public virtual string LambdaString_IndexOf(string operand, string value, string startIndex) => string.IsNullOrEmpty(startIndex) ? $"(charindex({operand}, {value})-1)" : $"(charindex({operand}, {value}, {startIndex})-1)";
 | 
			
		||||
        public virtual string LambdaString_PadLeft(string operand, string length, string paddingChar) => string.IsNullOrEmpty(paddingChar) ? $"lpad({operand}, {length})" : $"lpad({operand}, {length}, {paddingChar})";
 | 
			
		||||
        public virtual string LambdaString_PadRight(string operand, string length, string paddingChar) => string.IsNullOrEmpty(paddingChar) ? $"rpad({operand}, {length})" : $"rpad({operand}, {length}, {paddingChar})";
 | 
			
		||||
        public virtual string LambdaString_Trim(string operand) => $"ltrim(rtrim({operand}))";
 | 
			
		||||
        public virtual string LambdaString_TrimStart(string operand) => $"ltrim({operand})";
 | 
			
		||||
        public virtual string LambdaString_TrimEnd(string operand) => $"rtrim({operand})";
 | 
			
		||||
        public virtual string LambdaString_Replace(string operand, string oldValue, string newValue) => $"replace({operand}, {oldValue}, {newValue})";
 | 
			
		||||
        public virtual string LambdaString_CompareTo(string operand, string value) => $"({operand} - {value})";
 | 
			
		||||
        public virtual string LambdaString_Equals(string operand, string value) => $"({operand} = {value})";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaDateTime_Now => "getdate()";
 | 
			
		||||
        public virtual string LambdaDateTime_UtcNow => "getutcdate()";
 | 
			
		||||
        public virtual string LambdaDateTime_Today => "convert(char(10),getdate(),120)";
 | 
			
		||||
        public virtual string LambdaDateTime_MinValue => "'1753/1/1 0:00:00'";
 | 
			
		||||
        public virtual string LambdaDateTime_MaxValue => "'9999/12/31 23:59:59'";
 | 
			
		||||
        public virtual string LambdaDateTime_Date(string operand) => $"convert(char(10),{operand},120)";
 | 
			
		||||
        public virtual string LambdaDateTime_TimeOfDay(string operand) => $"datediff(second, convert(char(10),{operand},120), {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_DayOfWeek(string operand) => $"(datepart(weekday, {operand})-1)";
 | 
			
		||||
        public virtual string LambdaDateTime_Day(string operand) => $"datepart(day, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_DayOfYear(string operand) => $"datepart(dayofyear, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Month(string operand) => $"datepart(month, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Year(string operand) => $"datepart(year, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Hour(string operand) => $"datepart(hour, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Minute(string operand) => $"datepart(minute, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Second(string operand) => $"datepart(second, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Millisecond(string operand) => $"(datepart(millisecond, {operand})/1000)";
 | 
			
		||||
        public virtual string LambdaDateTime_Ticks(string operand) => $"(cast(datediff(second, '1970-1-1', {operand}) as bigint)*10000000+621355968000000000)";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaDateTime_DaysInMonth(string year, string month) => $"datepart(day, dateadd(day, -1, dateadd(month, 1, cast({year} as varchar) + '-' + cast({month} as varchar) + '-1')))";
 | 
			
		||||
        public virtual string LambdaDateTime_IsLeapYear(string year) => $"(({year})%4=0 AND ({year})%100<>0 OR ({year})%400=0)";
 | 
			
		||||
        public virtual string LambdaDateTime_Add(string operand, string value) => $"dateadd(second, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddDays(string operand, string value) => $"dateadd(day, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddHours(string operand, string value) => $"dateadd(hour, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddMilliseconds(string operand, string value) => $"dateadd(second, ({value})/1000, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddMinutes(string operand, string value) => $"dateadd(minute, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddMonths(string operand, string value) => $"dateadd(month, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddSeconds(string operand, string value) => $"dateadd(second, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddTicks(string operand, string value) => $"dateadd(second, ({value})/10000000, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_AddYears(string operand, string value) => $"dateadd(year, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Subtract(string operand, string value) => $"datediff(second, {value}, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_SubtractTimeSpan(string operand, string value) => $"dateadd(second, ({value})*-1, {operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_Equals(string operand, string value) => $"({operand} = {value})";
 | 
			
		||||
        public virtual string LambdaDateTime_CompareTo(string operand, string value) => $"datediff(second,{value},{operand})";
 | 
			
		||||
        public virtual string LambdaDateTime_ToString(string operand) => $"convert(varchar, {operand}, 121)";
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaMath_Abs(string operand) => $"abs({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Sign(string operand) => $"sign({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Floor(string operand) => $"floor({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Ceiling(string operand) => $"ceiling({ operand})";
 | 
			
		||||
        public virtual string LambdaMath_Round(string operand, string decimals) => $"round({operand}, {decimals})";
 | 
			
		||||
        public virtual string LambdaMath_Exp(string operand) => $"exp({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Log(string operand) => $"log({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Log10(string operand) => $"log10({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Pow(string operand, string y) => $"power({operand}, {y})";
 | 
			
		||||
        public virtual string LambdaMath_Sqrt(string operand) => $"sqrt({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Cos(string operand) => $"cos({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Sin(string operand) => $"sin({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Tan(string operand) => $"tan({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Acos(string operand) => $"acos({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Asin(string operand) => $"asin({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Atan(string operand) => $"atan({operand})";
 | 
			
		||||
        public virtual string LambdaMath_Atan2(string operand, string x) => $"atan2({operand}, {x})";
 | 
			
		||||
        public virtual string LambdaMath_Truncate(string operand) => $"floor({operand})";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcAdo/OdbcAdo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcAdo/OdbcAdo.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using SafeObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
    class OdbcAdo : FreeSql.Internal.CommonProvider.AdoProvider
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcAdo() : base(DataType.Odbc) { }
 | 
			
		||||
        public OdbcAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.Odbc)
 | 
			
		||||
        {
 | 
			
		||||
            base._util = util;
 | 
			
		||||
            if (!string.IsNullOrEmpty(masterConnectionString))
 | 
			
		||||
                MasterPool = new OdbcConnectionPool("主库", masterConnectionString, null, null);
 | 
			
		||||
            if (slaveConnectionStrings != null)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var slaveConnectionString in slaveConnectionStrings)
 | 
			
		||||
                {
 | 
			
		||||
                    var slavePool = new OdbcConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
 | 
			
		||||
                    SlavePools.Add(slavePool);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        OdbcAdapter Adapter => (_util == null ? FreeSqlOdbcGlobalExtensions.DefaultOdbcAdapter : _util._orm.GetOdbcAdapter());
 | 
			
		||||
 | 
			
		||||
        static DateTime dt1970 = new DateTime(1970, 1, 1);
 | 
			
		||||
        public override object AddslashesProcessParam(object param, Type mapType)
 | 
			
		||||
        {
 | 
			
		||||
            if (param == null) return "NULL";
 | 
			
		||||
            if (mapType != null && mapType != param.GetType())
 | 
			
		||||
                param = Utils.GetDataReaderValue(mapType, param);
 | 
			
		||||
            if (param is bool || param is bool?)
 | 
			
		||||
                return (bool)param ? 1 : 0;
 | 
			
		||||
            else if (param is string)
 | 
			
		||||
                return Adapter.UnicodeStringRawSql(param);
 | 
			
		||||
            else if (param is char)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
            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 Adapter.DateTimeRawSql(param);
 | 
			
		||||
            else if (param is TimeSpan || param is TimeSpan?)
 | 
			
		||||
                return Adapter.TimeSpanRawSql(param);
 | 
			
		||||
            else if (param is IEnumerable)
 | 
			
		||||
            {
 | 
			
		||||
                var sb = new StringBuilder();
 | 
			
		||||
                var ie = param as IEnumerable;
 | 
			
		||||
                foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
 | 
			
		||||
                return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
 | 
			
		||||
            }
 | 
			
		||||
            return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DbCommand CreateCommand()
 | 
			
		||||
        {
 | 
			
		||||
            return new OdbcCommand();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            (pool as OdbcConnectionPool).Return(conn, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,236 @@
 | 
			
		||||
using SafeObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcConnectionPool : ObjectPool<DbConnection>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        internal Action availableHandler;
 | 
			
		||||
        internal Action unavailableHandler;
 | 
			
		||||
 | 
			
		||||
        public OdbcConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null)
 | 
			
		||||
        {
 | 
			
		||||
            var policy = new OdbcConnectionPoolPolicy
 | 
			
		||||
            {
 | 
			
		||||
                _pool = this,
 | 
			
		||||
                Name = name
 | 
			
		||||
            };
 | 
			
		||||
            this.Policy = policy;
 | 
			
		||||
            policy.ConnectionString = connectionString;
 | 
			
		||||
 | 
			
		||||
            this.availableHandler = availableHandler;
 | 
			
		||||
            this.unavailableHandler = unavailableHandler;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false)
 | 
			
		||||
        {
 | 
			
		||||
            if (exception != null && exception is OdbcException)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (obj.Value.Ping() == false)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    base.SetUnavailable(exception);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            base.Return(obj, isRecreate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class OdbcConnectionPoolPolicy : IPolicy<DbConnection>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        internal OdbcConnectionPool _pool;
 | 
			
		||||
        public string Name { get; set; } = "Default OdbcConnection 对象池";
 | 
			
		||||
        public int PoolSize { get; set; } = 100;
 | 
			
		||||
        public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
 | 
			
		||||
        public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
 | 
			
		||||
        public int AsyncGetCapacity { get; set; } = 10000;
 | 
			
		||||
        public bool IsThrowGetTimeoutException { get; set; } = true;
 | 
			
		||||
        public int CheckAvailableInterval { get; set; } = 5;
 | 
			
		||||
 | 
			
		||||
        static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
        private string _connectionString;
 | 
			
		||||
        public string ConnectionString
 | 
			
		||||
        {
 | 
			
		||||
            get => _connectionString;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                _connectionString = value ?? "";
 | 
			
		||||
 | 
			
		||||
                var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
 | 
			
		||||
                Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
 | 
			
		||||
                if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
 | 
			
		||||
                var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
 | 
			
		||||
                PoolSize = poolsize + connStrIncr;
 | 
			
		||||
                _connectionString = m.Success ?
 | 
			
		||||
                    Regex.Replace(_connectionString, pattern, $"Max pool size={PoolSize}", RegexOptions.IgnoreCase) :
 | 
			
		||||
                    $"{_connectionString};Max pool size={PoolSize}";
 | 
			
		||||
 | 
			
		||||
                pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
 | 
			
		||||
                m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
 | 
			
		||||
                if (m.Success)
 | 
			
		||||
                {
 | 
			
		||||
                    IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
 | 
			
		||||
                    _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var minPoolSize = 0;
 | 
			
		||||
                pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
 | 
			
		||||
                m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
 | 
			
		||||
                if (m.Success)
 | 
			
		||||
                {
 | 
			
		||||
                    minPoolSize = int.Parse(m.Groups[1].Value);
 | 
			
		||||
                    _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool OnCheckAvailable(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
 | 
			
		||||
            return obj.Value.Ping(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DbConnection OnCreate()
 | 
			
		||||
        {
 | 
			
		||||
            var conn = new OdbcConnection(_connectionString);
 | 
			
		||||
            return conn;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnDestroy(DbConnection obj)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj.State != ConnectionState.Closed) obj.Close();
 | 
			
		||||
            obj.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnGet(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (_pool.IsAvailable)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (obj.Value == null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
 | 
			
		||||
                        throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        obj.Value.Open();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_pool.SetUnavailable(ex) == true)
 | 
			
		||||
                            throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async public Task OnGetAsync(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (_pool.IsAvailable)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (obj.Value == null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
 | 
			
		||||
                        throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (await obj.Value.PingAsync()) == false)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        await obj.Value.OpenAsync();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_pool.SetUnavailable(ex) == true)
 | 
			
		||||
                            throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnGetTimeout()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnReturn(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj.Value.State != ConnectionState.Closed) try { obj.Value.Close(); } catch { }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnAvailable()
 | 
			
		||||
        {
 | 
			
		||||
            _pool.availableHandler?.Invoke();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnUnavailable()
 | 
			
		||||
        {
 | 
			
		||||
            _pool.unavailableHandler?.Invoke();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class DbConnectionExtensions
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        static DbCommand PingCommand(DbConnection conn)
 | 
			
		||||
        {
 | 
			
		||||
            var cmd = conn.CreateCommand();
 | 
			
		||||
            cmd.CommandTimeout = 5;
 | 
			
		||||
            cmd.CommandText = "select 1";
 | 
			
		||||
            return cmd;
 | 
			
		||||
        }
 | 
			
		||||
        public static bool Ping(this DbConnection that, bool isThrow = false)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                PingCommand(that).ExecuteNonQuery();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
 | 
			
		||||
                if (isThrow) throw;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await PingCommand(that).ExecuteNonQueryAsync();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
 | 
			
		||||
                if (isThrow) throw;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcCodeFirst.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcCodeFirst.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcCodeFirst : Internal.CommonProvider.CodeFirstProvider
 | 
			
		||||
    {
 | 
			
		||||
        public override bool IsAutoSyncStructure { get => false; set => base.IsAutoSyncStructure = false; }
 | 
			
		||||
        public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
 | 
			
		||||
        public OdbcCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
            _utils = _commonUtils as OdbcUtils;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        OdbcUtils _utils;
 | 
			
		||||
        object _dicCsToDbLock = new object();
 | 
			
		||||
        Dictionary<string, (OdbcType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb;
 | 
			
		||||
 | 
			
		||||
        public override (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type)
 | 
			
		||||
        {
 | 
			
		||||
            if (_dicCsToDb == null)
 | 
			
		||||
            {
 | 
			
		||||
                lock (_dicCsToDbLock)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_dicCsToDb == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var reg = new Regex(@"\([^\)]+\)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
 | 
			
		||||
                        Func<string, string> deleteBrackets = str => reg.Replace(str, "");
 | 
			
		||||
 | 
			
		||||
                        _dicCsToDb = new Dictionary<string, (OdbcType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
 | 
			
		||||
                            { typeof(bool).FullName,  (OdbcType.Bit, _utils.Adapter.MappingOdbcTypeBit,$"{_utils.Adapter.MappingOdbcTypeBit} NOT NULL", null, false, false) },{ typeof(bool?).FullName,  (OdbcType.Bit, _utils.Adapter.MappingOdbcTypeBit,_utils.Adapter.MappingOdbcTypeBit, null, true, null) },
 | 
			
		||||
 | 
			
		||||
                            { typeof(sbyte).FullName,  (OdbcType.SmallInt, _utils.Adapter.MappingOdbcTypeSmallInt, $"{_utils.Adapter.MappingOdbcTypeSmallInt} NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName,  (OdbcType.SmallInt, _utils.Adapter.MappingOdbcTypeSmallInt, _utils.Adapter.MappingOdbcTypeSmallInt, false, true, null) },
 | 
			
		||||
                            { typeof(short).FullName,  (OdbcType.SmallInt, _utils.Adapter.MappingOdbcTypeSmallInt,$"{_utils.Adapter.MappingOdbcTypeSmallInt} NOT NULL", false, false, 0) },{ typeof(short?).FullName,  (OdbcType.SmallInt, _utils.Adapter.MappingOdbcTypeSmallInt, _utils.Adapter.MappingOdbcTypeSmallInt, false, true, null) },
 | 
			
		||||
                            { typeof(int).FullName,  (OdbcType.Int, _utils.Adapter.MappingOdbcTypeInt, $"{_utils.Adapter.MappingOdbcTypeInt} NOT NULL", false, false, 0) },{ typeof(int?).FullName,  (OdbcType.Int, _utils.Adapter.MappingOdbcTypeInt, _utils.Adapter.MappingOdbcTypeInt, false, true, null) },
 | 
			
		||||
                            { typeof(long).FullName,  (OdbcType.BigInt, _utils.Adapter.MappingOdbcTypeBigInt,$"{_utils.Adapter.MappingOdbcTypeBigInt} NOT NULL", false, false, 0) },{ typeof(long?).FullName,  (OdbcType.BigInt, _utils.Adapter.MappingOdbcTypeBigInt,_utils.Adapter.MappingOdbcTypeBigInt, false, true, null) },
 | 
			
		||||
 | 
			
		||||
                            { typeof(byte).FullName,  (OdbcType.TinyInt, _utils.Adapter.MappingOdbcTypeTinyInt,$"{_utils.Adapter.MappingOdbcTypeTinyInt} NOT NULL", true, false, 0) },{ typeof(byte?).FullName,  (OdbcType.TinyInt, _utils.Adapter.MappingOdbcTypeTinyInt,_utils.Adapter.MappingOdbcTypeTinyInt, true, true, null) },
 | 
			
		||||
                            { typeof(ushort).FullName,  (OdbcType.Int, _utils.Adapter.MappingOdbcTypeInt,$"{_utils.Adapter.MappingOdbcTypeInt} NOT NULL", true, false, 0) },{ typeof(ushort?).FullName,  (OdbcType.Int, _utils.Adapter.MappingOdbcTypeInt, _utils.Adapter.MappingOdbcTypeInt, true, true, null) },
 | 
			
		||||
                            { typeof(uint).FullName,  (OdbcType.BigInt, _utils.Adapter.MappingOdbcTypeBigInt, $"{_utils.Adapter.MappingOdbcTypeBigInt} NOT NULL", true, false, 0) },{ typeof(uint?).FullName,  (OdbcType.BigInt, _utils.Adapter.MappingOdbcTypeBigInt, _utils.Adapter.MappingOdbcTypeBigInt, true, true, null) },
 | 
			
		||||
                            { typeof(ulong).FullName,  (OdbcType.Decimal, _utils.Adapter.MappingOdbcTypeDecimal, $"{_utils.Adapter.MappingOdbcTypeDecimal}(20,0) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName,  (OdbcType.Decimal, _utils.Adapter.MappingOdbcTypeDecimal, $"{_utils.Adapter.MappingOdbcTypeDecimal}(20,0)", true, true, null) },
 | 
			
		||||
 | 
			
		||||
                            { typeof(double).FullName,  (OdbcType.Double, _utils.Adapter.MappingOdbcTypeDouble, $"{_utils.Adapter.MappingOdbcTypeDouble} NOT NULL", false, false, 0) },{ typeof(double?).FullName,  (OdbcType.Double, _utils.Adapter.MappingOdbcTypeDouble, _utils.Adapter.MappingOdbcTypeDouble, false, true, null) },
 | 
			
		||||
                            { typeof(float).FullName,  (OdbcType.Real, _utils.Adapter.MappingOdbcTypeReal,$"{_utils.Adapter.MappingOdbcTypeReal} NOT NULL", false, false, 0) },{ typeof(float?).FullName,  (OdbcType.Real, _utils.Adapter.MappingOdbcTypeReal,_utils.Adapter.MappingOdbcTypeReal, false, true, null) },
 | 
			
		||||
                            { typeof(decimal).FullName,  (OdbcType.Decimal, _utils.Adapter.MappingOdbcTypeDecimal, $"{_utils.Adapter.MappingOdbcTypeDecimal}(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName,  (OdbcType.Decimal, _utils.Adapter.MappingOdbcTypeDecimal, $"{_utils.Adapter.MappingOdbcTypeDecimal}(10,2)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                            { typeof(DateTime).FullName,  (OdbcType.DateTime, _utils.Adapter.MappingOdbcTypeDateTime, $"{_utils.Adapter.MappingOdbcTypeDateTime} NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName,  (OdbcType.DateTime, _utils.Adapter.MappingOdbcTypeDateTime, _utils.Adapter.MappingOdbcTypeDateTime, false, true, null) },
 | 
			
		||||
 | 
			
		||||
                            { typeof(byte[]).FullName,  (OdbcType.VarBinary, _utils.Adapter.MappingOdbcTypeVarBinary, $"{_utils.Adapter.MappingOdbcTypeVarBinary}(255)", false, null, new byte[0]) },
 | 
			
		||||
                            { typeof(string).FullName,  (OdbcType.VarChar, _utils.Adapter.MappingOdbcTypeVarChar, $"{_utils.Adapter.MappingOdbcTypeVarChar}(255)", false, null, "") },
 | 
			
		||||
 | 
			
		||||
                            { typeof(Guid).FullName,  (OdbcType.UniqueIdentifier, deleteBrackets(_utils.Adapter.MappingOdbcTypeUniqueIdentifier), $"{_utils.Adapter.MappingOdbcTypeUniqueIdentifier} NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName,  (OdbcType.UniqueIdentifier, deleteBrackets(_utils.Adapter.MappingOdbcTypeUniqueIdentifier), _utils.Adapter.MappingOdbcTypeUniqueIdentifier, false, true, null) },
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?, object)?(((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() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
 | 
			
		||||
            if (enumType != null)
 | 
			
		||||
            {
 | 
			
		||||
                var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
 | 
			
		||||
                    (OdbcType.BigInt, _utils.Adapter.MappingOdbcTypeBigInt, $"{_utils.Adapter.MappingOdbcTypeBigInt}{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
 | 
			
		||||
                    (OdbcType.Int, _utils.Adapter.MappingOdbcTypeInt, $"{_utils.Adapter.MappingOdbcTypeInt}{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
 | 
			
		||||
                if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    lock (_dicCsToDbLock)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                            _dicCsToDb.Add(type.FullName, newItem);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return ((int)newItem.Item1, newItem.Item2, newItem.Item3, newItem.Item5, newItem.Item6);
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string GetComparisonDDLStatements(params Type[] entityTypes) => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										441
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcExpression.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcExpression.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,441 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
    class OdbcExpression : CommonExpression
 | 
			
		||||
    {
 | 
			
		||||
        OdbcUtils _utils;
 | 
			
		||||
        public OdbcExpression(CommonUtils common) : base(common)
 | 
			
		||||
        {
 | 
			
		||||
            _utils = common as OdbcUtils;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            switch (exp.NodeType)
 | 
			
		||||
            {
 | 
			
		||||
                case ExpressionType.Convert:
 | 
			
		||||
                    var operandExp = (exp as UnaryExpression)?.Operand;
 | 
			
		||||
                    var gentype = exp.Type.NullableTypeOrThis();
 | 
			
		||||
                    if (gentype != operandExp.Type.NullableTypeOrThis())
 | 
			
		||||
                    {
 | 
			
		||||
                        switch (gentype.ToString())
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.Boolean": return _utils.Adapter.LambdaConvert_ToBoolean(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Byte": return _utils.Adapter.LambdaConvert_ToByte(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Char": return _utils.Adapter.LambdaConvert_ToChar(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.DateTime": return _utils.Adapter.LambdaConvert_ToDateTime(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Decimal": return _utils.Adapter.LambdaConvert_ToDecimal(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Double": return _utils.Adapter.LambdaConvert_ToDouble(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Int16": return _utils.Adapter.LambdaConvert_ToInt16(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Int32": return _utils.Adapter.LambdaConvert_ToInt32(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Int64": return _utils.Adapter.LambdaConvert_ToInt64(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.SByte": return _utils.Adapter.LambdaConvert_ToSByte(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Single": return _utils.Adapter.LambdaConvert_ToSingle(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.String": return _utils.Adapter.LambdaConvert_ToString(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.UInt16": return _utils.Adapter.LambdaConvert_ToUInt16(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.UInt32": return _utils.Adapter.LambdaConvert_ToUInt32(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.UInt64": return _utils.Adapter.LambdaConvert_ToUInt64(operandExp.Type, getExp(operandExp));
 | 
			
		||||
                            case "System.Guid": return _utils.Adapter.LambdaConvert_ToGuid(operandExp.Type, 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 _utils.Adapter.LambdaConvert_ToBoolean(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Byte": return _utils.Adapter.LambdaConvert_ToByte(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Char": return _utils.Adapter.LambdaConvert_ToChar(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.DateTime": return _utils.Adapter.LambdaConvert_ToDateTime(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Decimal": return _utils.Adapter.LambdaConvert_ToDecimal(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Double": return _utils.Adapter.LambdaConvert_ToDouble(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Int16": return _utils.Adapter.LambdaConvert_ToInt16(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Int32": return _utils.Adapter.LambdaConvert_ToInt32(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Int64": return _utils.Adapter.LambdaConvert_ToInt64(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.SByte": return _utils.Adapter.LambdaConvert_ToSByte(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Single": return _utils.Adapter.LambdaConvert_ToSingle(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.UInt16": return _utils.Adapter.LambdaConvert_ToUInt16(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.UInt32": return _utils.Adapter.LambdaConvert_ToUInt32(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.UInt64": return _utils.Adapter.LambdaConvert_ToUInt64(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                                case "System.Guid": return _utils.Adapter.LambdaConvert_ToGuid(callExp.Method.DeclaringType, getExp(callExp.Arguments[0]));
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "NewGuid":
 | 
			
		||||
                            switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
 | 
			
		||||
                            {
 | 
			
		||||
                                case "System.Guid": return _utils.Adapter.LambdaGuid_NewGuid;
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "Next":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return _utils.Adapter.LambdaRandom_Next;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "NextDouble":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return _utils.Adapter.LambdaRandom_NextDouble;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "Random":
 | 
			
		||||
                            if (callExp.Method.DeclaringType.IsNumberType()) return _utils.Adapter.LambdaRandom_NextDouble;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "ToString":
 | 
			
		||||
                            if (callExp.Object != null) return callExp.Arguments.Count == 0 ? _utils.Adapter.LambdaConvert_ToString(callExp.Object.Type, getExp(callExp.Object)) : null;
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    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 == null) objType = callExp.Method.DeclaringType;
 | 
			
		||||
                    if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType))
 | 
			
		||||
                    {
 | 
			
		||||
                        var left = objExp == null ? null : getExp(objExp);
 | 
			
		||||
                        switch (callExp.Method.Name)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "Contains":
 | 
			
		||||
                                //判断 in
 | 
			
		||||
                                return $"({getExp(callExp.Arguments[argIndex])}) in {left}";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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(",");
 | 
			
		||||
                        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 _utils.Adapter.LambdaString_Length(left);
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Now": return _utils.Adapter.LambdaDateTime_Now;
 | 
			
		||||
                    case "UtcNow": return _utils.Adapter.LambdaDateTime_UtcNow;
 | 
			
		||||
                    case "Today": return _utils.Adapter.LambdaDateTime_Today;
 | 
			
		||||
                    case "MinValue": return _utils.Adapter.LambdaDateTime_MinValue;
 | 
			
		||||
                    case "MaxValue": return _utils.Adapter.LambdaDateTime_MaxValue;
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Date": return _utils.Adapter.LambdaDateTime_Date(left);
 | 
			
		||||
                case "TimeOfDay": return _utils.Adapter.LambdaDateTime_TimeOfDay(left);
 | 
			
		||||
                case "DayOfWeek": return _utils.Adapter.LambdaDateTime_DayOfWeek(left);
 | 
			
		||||
                case "Day": return _utils.Adapter.LambdaDateTime_Day(left);
 | 
			
		||||
                case "DayOfYear": return _utils.Adapter.LambdaDateTime_DayOfYear(left);
 | 
			
		||||
                case "Month": return _utils.Adapter.LambdaDateTime_Month(left);
 | 
			
		||||
                case "Year": return _utils.Adapter.LambdaDateTime_Year(left);
 | 
			
		||||
                case "Hour": return _utils.Adapter.LambdaDateTime_Hour(left);
 | 
			
		||||
                case "Minute": return _utils.Adapter.LambdaDateTime_Minute(left);
 | 
			
		||||
                case "Second": return _utils.Adapter.LambdaDateTime_Second(left);
 | 
			
		||||
                case "Millisecond": return _utils.Adapter.LambdaDateTime_Millisecond(left);
 | 
			
		||||
                case "Ticks": return _utils.Adapter.LambdaDateTime_Ticks(left);
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Zero": return "0";
 | 
			
		||||
                    case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
 | 
			
		||||
                    case "MaxValue": return "922337203685477580";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Days": return _utils.Adapter.LambdaMath_Floor($"({left})/{60 * 60 * 24}");
 | 
			
		||||
                case "Hours": return _utils.Adapter.LambdaMath_Floor($"({left})/{60 * 60}%24");
 | 
			
		||||
                case "Milliseconds": return $"({_utils.Adapter.CastSql(left, _utils.Adapter.MappingOdbcTypeBigInt)}*1000)";
 | 
			
		||||
                case "Minutes": return _utils.Adapter.LambdaMath_Floor($"({left})/60%60");
 | 
			
		||||
                case "Seconds": return $"(({left})%60)";
 | 
			
		||||
                case "Ticks": return $"({_utils.Adapter.CastSql(left, _utils.Adapter.MappingOdbcTypeBigInt)}*10000000)";
 | 
			
		||||
                case "TotalDays": return $"(({left})/{60 * 60 * 24})";
 | 
			
		||||
                case "TotalHours": return $"(({left})/{60 * 60})";
 | 
			
		||||
                case "TotalMilliseconds": return $"({_utils.Adapter.CastSql(left, _utils.Adapter.MappingOdbcTypeBigInt)}*1000)";
 | 
			
		||||
                case "TotalMinutes": return $"(({left})/60)";
 | 
			
		||||
                case "TotalSeconds": return $"({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": return _utils.Adapter.LambdaString_IsNullOrEmpty(getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "IsNullOrWhiteSpace": return _utils.Adapter.LambdaString_IsNullOrWhiteSpace(getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "Concat": return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), exp.Arguments.Select(a => a.Type).ToArray());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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 (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"({_common.StringConcat(new string[] { args0Value, "'%'" }, new[] { typeof(int), typeof(string) })})")}";
 | 
			
		||||
                        if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"({_common.StringConcat(new string[] { "'%'", args0Value }, new[] { typeof(string), typeof(int) })})")}";
 | 
			
		||||
                        if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
 | 
			
		||||
                        return $"({left}) LIKE ({_common.StringConcat(new string[] { "'%'", args0Value, "'%'" }, new[] { typeof(string), typeof(int), typeof(string)})})";
 | 
			
		||||
                    case "ToLower": return _utils.Adapter.LambdaString_ToLower(left);
 | 
			
		||||
                    case "ToUpper": return _utils.Adapter.LambdaString_ToUpper(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 _utils.Adapter.LambdaString_Substring(left, substrArgs1, null);
 | 
			
		||||
                        return _utils.Adapter.LambdaString_Substring(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 _utils.Adapter.LambdaString_IndexOf(left, indexOfFindStr, locateArgs1);
 | 
			
		||||
                        }
 | 
			
		||||
                        return _utils.Adapter.LambdaString_IndexOf(left, indexOfFindStr, null);
 | 
			
		||||
                    case "PadLeft":
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return _utils.Adapter.LambdaString_PadLeft(left, getExp(exp.Arguments[0]), null);
 | 
			
		||||
                        return _utils.Adapter.LambdaString_PadLeft(left, getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                    case "PadRight":
 | 
			
		||||
                        if (exp.Arguments.Count == 1) return _utils.Adapter.LambdaString_PadRight(left, getExp(exp.Arguments[0]), null);
 | 
			
		||||
                        return _utils.Adapter.LambdaString_PadRight(left, getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                    case "Trim": return _utils.Adapter.LambdaString_Trim(left);
 | 
			
		||||
                    case "TrimStart": return _utils.Adapter.LambdaString_TrimStart(left);
 | 
			
		||||
                    case "TrimEnd": return _utils.Adapter.LambdaString_TrimEnd(left);
 | 
			
		||||
                    case "Replace": return _utils.Adapter.LambdaString_Replace(left, getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                    case "CompareTo": return _utils.Adapter.LambdaString_CompareTo(left, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "Equals": return _utils.Adapter.LambdaString_Equals(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 _utils.Adapter.LambdaMath_Abs(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Sign": return _utils.Adapter.LambdaMath_Sign(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Floor": return _utils.Adapter.LambdaMath_Floor(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Ceiling": return _utils.Adapter.LambdaMath_Ceiling(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Round":
 | 
			
		||||
                    if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return _utils.Adapter.LambdaMath_Round(getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                    return _utils.Adapter.LambdaMath_Round(getExp(exp.Arguments[0]), "0");
 | 
			
		||||
                case "Exp": return _utils.Adapter.LambdaMath_Exp(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Log": return _utils.Adapter.LambdaMath_Log(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Log10": return _utils.Adapter.LambdaMath_Log10(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Pow": return _utils.Adapter.LambdaMath_Pow(getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                case "Sqrt": return _utils.Adapter.LambdaMath_Sqrt(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Cos": return  _utils.Adapter.LambdaMath_Cos(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Sin": return  _utils.Adapter.LambdaMath_Sin(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Tan": return  _utils.Adapter.LambdaMath_Tan(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Acos": return _utils.Adapter.LambdaMath_Acos(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Asin": return _utils.Adapter.LambdaMath_Asin(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Atan": return _utils.Adapter.LambdaMath_Atan(getExp(exp.Arguments[0]));
 | 
			
		||||
                case "Atan2": return _utils.Adapter.LambdaMath_Atan2(getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                case "Truncate": return _utils.Adapter.LambdaMath_Truncate(getExp(exp.Arguments[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 _utils.Adapter.LambdaDateTime_CompareTo(getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                    case "DaysInMonth": return _utils.Adapter.LambdaDateTime_DaysInMonth(getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
                    case "Equals": return _utils.Adapter.LambdaDateTime_Equals(getExp(exp.Arguments[0]), getExp(exp.Arguments[1]));
 | 
			
		||||
 | 
			
		||||
                    case "IsLeapYear": return _utils.Adapter.LambdaDateTime_IsLeapYear(getExp(exp.Arguments[0]));
 | 
			
		||||
 | 
			
		||||
                    case "Parse": return _utils.Adapter.LambdaConvert_ToDateTime(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return _utils.Adapter.LambdaConvert_ToDateTime(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return _utils.Adapter.LambdaDateTime_Add(left, args1);
 | 
			
		||||
                    case "AddDays": return _utils.Adapter.LambdaDateTime_AddDays(left, args1);
 | 
			
		||||
                    case "AddHours": return _utils.Adapter.LambdaDateTime_AddHours(left, args1);
 | 
			
		||||
                    case "AddMilliseconds": return _utils.Adapter.LambdaDateTime_AddMilliseconds(left, args1);
 | 
			
		||||
                    case "AddMinutes": return _utils.Adapter.LambdaDateTime_AddMinutes(left, args1);
 | 
			
		||||
                    case "AddMonths": return _utils.Adapter.LambdaDateTime_AddMonths(left, args1);
 | 
			
		||||
                    case "AddSeconds": return _utils.Adapter.LambdaDateTime_AddSeconds(left, args1);
 | 
			
		||||
                    case "AddTicks": return _utils.Adapter.LambdaDateTime_AddTicks(left, args1);
 | 
			
		||||
                    case "AddYears": return _utils.Adapter.LambdaDateTime_AddYears(left, args1);
 | 
			
		||||
                    case "Subtract":
 | 
			
		||||
                        switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.DateTime": return _utils.Adapter.LambdaDateTime_Subtract(left, args1);
 | 
			
		||||
                            case "System.TimeSpan": return _utils.Adapter.LambdaDateTime_SubtractTimeSpan(left, args1);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return _utils.Adapter.LambdaDateTime_Equals(left, args1);
 | 
			
		||||
                    case "CompareTo": return _utils.Adapter.LambdaDateTime_CompareTo(left, args1);
 | 
			
		||||
                    case "ToString": return exp.Arguments.Count == 0 ? _utils.Adapter.LambdaDateTime_ToString(left) : null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual string LambdaDateSpan_Add(string operand, string value) => $"({operand}+{value})";
 | 
			
		||||
        public virtual string LambdaDateSpan_Subtract(string operand, string value) => $"({operand}-({value}))";
 | 
			
		||||
        public virtual string LambdaDateSpan_Equals(string oldvalue, string newvalue) => $"({oldvalue} = {newvalue})";
 | 
			
		||||
        public virtual string LambdaDateSpan_CompareTo(string oldvalue, string newvalue) => $"({oldvalue}-({newvalue}))";
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})";
 | 
			
		||||
                    case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})";
 | 
			
		||||
                    case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)";
 | 
			
		||||
                    case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)";
 | 
			
		||||
                    case "FromSeconds": return $"({getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)";
 | 
			
		||||
                    case "Parse": return _utils.Adapter.LambdaConvert_ToInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return _utils.Adapter.LambdaConvert_ToInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({args1}))";
 | 
			
		||||
                    case "ToString": return _utils.Adapter.LambdaConvert_ToString(exp.Type, 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 _utils.Adapter.LambdaConvert_ToBoolean(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToByte": return _utils.Adapter.LambdaConvert_ToByte(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToChar": return _utils.Adapter.LambdaConvert_ToChar(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToDateTime": return _utils.Adapter.LambdaConvert_ToDateTime(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToDecimal": return _utils.Adapter.LambdaConvert_ToDecimal(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToDouble": return _utils.Adapter.LambdaConvert_ToDouble(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToInt16": return _utils.Adapter.LambdaConvert_ToInt16(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToInt32": return _utils.Adapter.LambdaConvert_ToInt32(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToInt64": return _utils.Adapter.LambdaConvert_ToInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToSByte": return _utils.Adapter.LambdaConvert_ToSByte(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToSingle": return _utils.Adapter.LambdaConvert_ToSingle(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToString": return _utils.Adapter.LambdaConvert_ToString(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToUInt16": return _utils.Adapter.LambdaConvert_ToUInt16(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToUInt32": return _utils.Adapter.LambdaConvert_ToUInt32(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                    case "ToUInt64": return _utils.Adapter.LambdaConvert_ToUInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0]));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcProvider.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.CommonProvider;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using FreeSql.Odbc.Default;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class OdbcProvider<TMark> : IFreeSql<TMark>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public ISelect<T1> Select<T1>() where T1 : class => new OdbcSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
 | 
			
		||||
        public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new OdbcSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public IInsert<T1> Insert<T1>() where T1 : class => new OdbcInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        public IInsert<T1> Insert<T1>(T1 source) where T1 : class => this.Insert<T1>().AppendData(source);
 | 
			
		||||
        public IInsert<T1> Insert<T1>(T1[] source) where T1 : class => this.Insert<T1>().AppendData(source);
 | 
			
		||||
        public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => this.Insert<T1>().AppendData(source);
 | 
			
		||||
        public IUpdate<T1> Update<T1>() where T1 : class => new OdbcUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
 | 
			
		||||
        public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new OdbcUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public IDelete<T1> Delete<T1>() where T1 : class => new OdbcDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
 | 
			
		||||
        public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new OdbcDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
 | 
			
		||||
        public IAdo Ado { get; }
 | 
			
		||||
        public IAop Aop { get; }
 | 
			
		||||
        public ICodeFirst CodeFirst { get; }
 | 
			
		||||
        public IDbFirst DbFirst => throw new NotImplementedException("FreeSql.Odbc.Default 未实现该功能");
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 生成一个普通访问功能的 IFreeSql 对象,用来访问 odbc 
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="masterConnectionString"></param>
 | 
			
		||||
        /// <param name="slaveConnectionString"></param>
 | 
			
		||||
        /// <param name="adapter">适配器</param>
 | 
			
		||||
        public OdbcProvider(string masterConnectionString, string[] slaveConnectionString)
 | 
			
		||||
        {
 | 
			
		||||
            this.InternalCommonUtils = new OdbcUtils(this);
 | 
			
		||||
            this.InternalCommonExpression = new OdbcExpression(this.InternalCommonUtils);
 | 
			
		||||
 | 
			
		||||
            this.Ado = new OdbcAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
 | 
			
		||||
            this.Aop = new AopProvider();
 | 
			
		||||
 | 
			
		||||
            this.CodeFirst = new OdbcCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
 | 
			
		||||
            var _utils = InternalCommonUtils as OdbcUtils;
 | 
			
		||||
            //处理 MaxLength
 | 
			
		||||
            this.Aop.ConfigEntityProperty += new EventHandler<Aop.ConfigEntityPropertyEventArgs>((s, e) =>
 | 
			
		||||
            {
 | 
			
		||||
                object[] attrs = null;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    attrs = e.Property.GetCustomAttributes(false).ToArray(); //.net core 反射存在版本冲突问题,导致该方法异常
 | 
			
		||||
                }
 | 
			
		||||
                catch { }
 | 
			
		||||
 | 
			
		||||
                var maxlenAttr = attrs.Where(a => {
 | 
			
		||||
                    return ((a as Attribute)?.TypeId as Type)?.Name == "MaxLengthAttribute";
 | 
			
		||||
                }).FirstOrDefault();
 | 
			
		||||
                if (maxlenAttr != null)
 | 
			
		||||
                {
 | 
			
		||||
                    var lenProp = maxlenAttr.GetType().GetProperties().Where(a => a.PropertyType.IsNumberType()).FirstOrDefault();
 | 
			
		||||
                    if (lenProp != null && int.TryParse(string.Concat(lenProp.GetValue(maxlenAttr, null)), out var tryval))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (tryval != 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (this.Ado.DataType)
 | 
			
		||||
                            {
 | 
			
		||||
                                case DataType.Sqlite:
 | 
			
		||||
                                    e.ModifyResult.DbType = tryval > 0 ? $"{_utils.Adapter.MappingOdbcTypeVarChar}({tryval})" : _utils.Adapter.MappingOdbcTypeText;
 | 
			
		||||
                                    break;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal CommonUtils InternalCommonUtils { get; }
 | 
			
		||||
        internal CommonExpression InternalCommonExpression { get; }
 | 
			
		||||
 | 
			
		||||
        public void Transaction(Action handler) => Ado.Transaction(handler);
 | 
			
		||||
 | 
			
		||||
        public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
 | 
			
		||||
 | 
			
		||||
        ~OdbcProvider()
 | 
			
		||||
        {
 | 
			
		||||
            this.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
        bool _isdisposed = false;
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            if (_isdisposed) return;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                (this.Ado as AdoProvider)?.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                FreeSqlOdbcGlobalExtensions._dicOdbcAdater.TryRemove(this as IFreeSql, out var tryada);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
partial class FreeSqlOdbcGlobalExtensions
 | 
			
		||||
{
 | 
			
		||||
    internal static OdbcAdapter DefaultOdbcAdapter = new OdbcAdapter();
 | 
			
		||||
    internal static ConcurrentDictionary<IFreeSql, OdbcAdapter> _dicOdbcAdater = new ConcurrentDictionary<IFreeSql, OdbcAdapter>();
 | 
			
		||||
    public static void SetOdbcAdapter(this IFreeSql that, OdbcAdapter adapter) => _dicOdbcAdater.AddOrUpdate(that, adapter, (fsql, old) => adapter);
 | 
			
		||||
    internal static OdbcAdapter GetOdbcAdapter(this IFreeSql that) => _dicOdbcAdater.TryGetValue(that, out var tryada) ? tryada : DefaultOdbcAdapter;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcUtils.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								Providers/FreeSql.Provider.Odbc/Default/OdbcUtils.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.Default
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class OdbcUtils : CommonUtils
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcUtils(IFreeSql orm) : base(orm) { }
 | 
			
		||||
        public OdbcAdapter Adapter => _orm.GetOdbcAdapter();
 | 
			
		||||
 | 
			
		||||
        public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
 | 
			
		||||
            if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
 | 
			
		||||
            var ret = new OdbcParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
 | 
			
		||||
            var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
            if (tp != null) ret.OdbcType = (OdbcType)tp.Value;
 | 
			
		||||
            _params?.Add(ret);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
 | 
			
		||||
            Utils.GetDbParamtersByObject<OdbcParameter>(sql, obj, null, (name, type, value) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
 | 
			
		||||
                var ret = new OdbcParameter { ParameterName = $"@{name}", Value = value };
 | 
			
		||||
                var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
                if (tp != null) ret.OdbcType = (OdbcType)tp.Value;
 | 
			
		||||
                return ret;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbc(args);
 | 
			
		||||
        public override string QuoteSqlName(string name)
 | 
			
		||||
        {
 | 
			
		||||
            var nametrim = name.Trim();
 | 
			
		||||
            if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                return nametrim; //原生SQL
 | 
			
		||||
            //return $"[{nametrim.TrimStart('[').TrimEnd(']').Replace(".", "].[")}]";
 | 
			
		||||
            return $"{Adapter.QuoteSqlNameLeft}{nametrim.TrimStart(Adapter.QuoteSqlNameLeft).TrimEnd(Adapter.QuoteSqlNameRight).Replace(".", $"{Adapter.QuoteSqlNameRight}.{Adapter.QuoteSqlNameLeft}")}{Adapter.QuoteSqlNameRight}";
 | 
			
		||||
        }
 | 
			
		||||
        public override string TrimQuoteSqlName(string name)
 | 
			
		||||
        {
 | 
			
		||||
            var nametrim = name.Trim();
 | 
			
		||||
            if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                return nametrim; //原生SQL
 | 
			
		||||
            //return $"{nametrim.TrimStart('[').TrimEnd(']').Replace("].[", ".").Replace(".[", ".")}";
 | 
			
		||||
            return $"{nametrim.TrimStart(Adapter.QuoteSqlNameLeft).TrimEnd(Adapter.QuoteSqlNameRight).Replace($"{Adapter.QuoteSqlNameRight}.{Adapter.QuoteSqlNameLeft}", ".").Replace($".{Adapter.QuoteSqlNameLeft}", ".")}";
 | 
			
		||||
        }
 | 
			
		||||
        public override string QuoteParamterName(string name) => $"@{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
 | 
			
		||||
        public override string IsNull(string sql, object value) => Adapter.IsNullSql(sql, value);
 | 
			
		||||
        public override string StringConcat(string[] objs, Type[] types) => Adapter.ConcatSql(objs, types);
 | 
			
		||||
        public override string Mod(string left, string right, Type leftType, Type rightType) => Adapter.Mod(left, right, leftType, rightType);
 | 
			
		||||
        public override string Div(string left, string right, Type leftType, Type rightType) => Adapter.Div(left, right, leftType, rightType);
 | 
			
		||||
 | 
			
		||||
        public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
 | 
			
		||||
        public override string QuoteReadColumn(Type type, string columnName) => Adapter.FieldSql(type, columnName);
 | 
			
		||||
 | 
			
		||||
        public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (value == null) return "NULL";
 | 
			
		||||
            if (type == typeof(byte[])) return Adapter.ByteRawSql(value);
 | 
			
		||||
            return FormatSql("{0}", value, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
 | 
			
		||||
		<Version>0.9.17</Version>
 | 
			
		||||
		<Version>0.9.18</Version>
 | 
			
		||||
		<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
			
		||||
		<Authors>YeXiangQin</Authors>
 | 
			
		||||
		<Description>FreeSql 数据库 Odbc 实现,基于 {Oracle}、{SQL Server}、{MySQL ODBC 8.0 Unicode Driver}</Description>
 | 
			
		||||
		<Description>FreeSql 数据库 Odbc 实现,基于 {Oracle}、{SQL Server}、{MySQL ODBC 8.0 Unicode Driver}、{PostgreSQL Unicode(x64)} 专用访问实现,以及通用 Odbc 访问所有数据库</Description>
 | 
			
		||||
		<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
 | 
			
		||||
		<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
 | 
			
		||||
		<RepositoryType>git</RepositoryType>
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,8 @@
 | 
			
		||||
    /// <param name="that"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static string FormatOdbcSqlServer(this string that, params object[] args) => _sqlserverAdo.Addslashes(that, args);
 | 
			
		||||
    static FreeSql.Odbc.SqlServer.OdbcSqlServerAdo _sqlserverAdo = new FreeSql.Odbc.SqlServer.OdbcSqlServerAdo();
 | 
			
		||||
    public static string FormatOdbcSqlServer(this string that, params object[] args) => _odbcSqlServerAdo.Addslashes(that, args);
 | 
			
		||||
    static FreeSql.Odbc.SqlServer.OdbcSqlServerAdo _odbcSqlServerAdo = new FreeSql.Odbc.SqlServer.OdbcSqlServerAdo();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
 | 
			
		||||
@@ -25,6 +25,24 @@
 | 
			
		||||
    /// <param name="that"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static string FormatOdbcMySql(this string that, params object[] args) => _mysqlAdo.Addslashes(that, args);
 | 
			
		||||
    static FreeSql.Odbc.MySql.OdbcMySqlAdo _mysqlAdo = new FreeSql.Odbc.MySql.OdbcMySqlAdo();
 | 
			
		||||
    public static string FormatOdbcMySql(this string that, params object[] args) => _odbcMySqlAdo.Addslashes(that, args);
 | 
			
		||||
    static FreeSql.Odbc.MySql.OdbcMySqlAdo _odbcMySqlAdo = new FreeSql.Odbc.MySql.OdbcMySqlAdo();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="that"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static string FormatOdbcPostgreSQL(this string that, params object[] args) => _odbcPostgreSQLAdo.Addslashes(that, args);
 | 
			
		||||
    static FreeSql.Odbc.PostgreSQL.OdbcPostgreSQLAdo _odbcPostgreSQLAdo = new FreeSql.Odbc.PostgreSQL.OdbcPostgreSQLAdo();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="that"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static string FormatOdbc(this string that, params object[] args) => _odbcAdo.Addslashes(that, args);
 | 
			
		||||
    static FreeSql.Odbc.Default.OdbcAdo _odbcAdo = new FreeSql.Odbc.Default.OdbcAdo();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -114,60 +114,60 @@ namespace FreeSql.Odbc.MySql
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public OdbcMySqlSelect(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 MySqlSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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 MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); OdbcMySqlSelect<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(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    {
 | 
			
		||||
        public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class 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 MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class MySqlSelect<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 T1 : class 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
 | 
			
		||||
    class OdbcMySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T1 : class 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 MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcMySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcMySqlSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,10 @@
 | 
			
		||||
using FreeSql.DataAnnotations;
 | 
			
		||||
using FreeSql.DatabaseModel;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.MySql
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -383,7 +383,7 @@ namespace FreeSql.Odbc.MySql
 | 
			
		||||
                            case "System.TimeSpan": return $"date_sub({left}, interval ({args1}) microsecond)";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"timestampdiff(microsecond,{args1},{left})";
 | 
			
		||||
                    case "ToString": return exp.Arguments.Count == 0 ? $"date_format({left}, '%Y-%m-%d %H:%i:%s.%f')" : null;
 | 
			
		||||
                }
 | 
			
		||||
@@ -419,8 +419,8 @@ namespace FreeSql.Odbc.MySql
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({args1}))";
 | 
			
		||||
                    case "ToString": return $"cast({left} as char)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -126,60 +126,60 @@ namespace FreeSql.Odbc.Oracle
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public OdbcOracleSelect(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 OracleSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<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 OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); OdbcOracleSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
 | 
			
		||||
        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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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); OdbcOracleSelect<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(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    class OdbcOracleSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class
 | 
			
		||||
    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 T1 : class 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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OracleSelect<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 T1 : class 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
 | 
			
		||||
    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 T1 : class 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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public OdbcOracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcOracleSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
using FreeSql.DataAnnotations;
 | 
			
		||||
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.Data.Odbc;
 | 
			
		||||
 
 | 
			
		||||
@@ -385,8 +385,8 @@ namespace FreeSql.Odbc.Oracle
 | 
			
		||||
                            case "System.TimeSpan": return $"({left}-{args1})";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "CompareTo": return $"extract(day from ({left}-({getExp(exp.Arguments[0])})))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"extract(day from ({left}-({args1})))";
 | 
			
		||||
                    case "ToString": return exp.Arguments.Count == 0 ? $"to_char({left},'YYYY-MM-DD HH24:MI:SS.FF6')" : null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -421,8 +421,8 @@ namespace FreeSql.Odbc.Oracle
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "CompareTo": return $"extract(day from ({left}-({getExp(exp.Arguments[0])})))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"extract(day from ({left}-({args1})))";
 | 
			
		||||
                    case "ToString": return $"to_char({left})";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,95 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override List<T1> ExecuteDeleted()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var dbParms = _params.ToArray();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            this.ClearData();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async public override Task<List<T1>> ExecuteDeletedAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var dbParms = _params.ToArray();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            this.ClearData();
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,199 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(5000, 3000);
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(5000, 3000);
 | 
			
		||||
        public override long ExecuteIdentity() => base.SplitExecuteIdentity(5000, 3000);
 | 
			
		||||
        public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(5000, 3000);
 | 
			
		||||
        public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(5000, 3000);
 | 
			
		||||
        public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(5000, 3000);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        protected override long RawExecuteIdentity()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            Aop.CurdBeforeEventArgs before = null;
 | 
			
		||||
 | 
			
		||||
            var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
 | 
			
		||||
            if (identCols.Any() == false)
 | 
			
		||||
            {
 | 
			
		||||
                before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
                _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
 | 
			
		||||
            before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async protected override Task<long> RawExecuteIdentityAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return 0;
 | 
			
		||||
 | 
			
		||||
            long ret = 0;
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            Aop.CurdBeforeEventArgs before = null;
 | 
			
		||||
 | 
			
		||||
            var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
 | 
			
		||||
            if (identCols.Any() == false)
 | 
			
		||||
            {
 | 
			
		||||
                before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
                _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                    _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
                }
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
 | 
			
		||||
            before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteInserted()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, _params);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async protected override Task<List<T1>> RawExecuteInsertedAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, _params);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,175 @@
 | 
			
		||||
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.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        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, List<LambdaExpression> _whereCascadeExpression, IFreeSql _orm)
 | 
			
		||||
        {
 | 
			
		||||
            if (_orm.CodeFirst.IsAutoSyncStructure)
 | 
			
		||||
                _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
 | 
			
		||||
 | 
			
		||||
            if (_whereCascadeExpression.Any())
 | 
			
		||||
                foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
 | 
			
		||||
                    tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereCascadeExpression);
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var tbUnionsGt0 = tbUnions.Count > 1;
 | 
			
		||||
            for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
 | 
			
		||||
            {
 | 
			
		||||
                if (tbUnionsIdx > 0) sb.Append(" \r\n\r\nUNION ALL\r\n\r\n");
 | 
			
		||||
                if (tbUnionsGt0) sb.Append("select * from (");
 | 
			
		||||
                var tbUnion = tbUnions[tbUnionsIdx];
 | 
			
		||||
 | 
			
		||||
                var sbnav = new StringBuilder();
 | 
			
		||||
                sb.Append(_select);
 | 
			
		||||
                if (_distinct) sb.Append("DISTINCT ");
 | 
			
		||||
                sb.Append(field).Append(" \r\nFROM ");
 | 
			
		||||
                var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
 | 
			
		||||
                var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
 | 
			
		||||
                for (var a = 0; a < tbsfrom.Length; a++)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(tbsfrom[a].Alias);
 | 
			
		||||
                    if (tbsjoin.Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
 | 
			
		||||
                        for (var b = 1; b < tbsfrom.Length; b++)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(tbsfrom[b].Alias);
 | 
			
		||||
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
 | 
			
		||||
                                if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false) sb.Append(" AND (").Append(tbsfrom[b].Cascade).Append(")");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        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).Append(")");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (a < tbsfrom.Length - 1) sb.Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var tb in tbsjoin)
 | 
			
		||||
                {
 | 
			
		||||
                    if (tb.Type == SelectTableInfoType.Parent) continue;
 | 
			
		||||
                    switch (tb.Type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case SelectTableInfoType.LeftJoin:
 | 
			
		||||
                            sb.Append(" \r\nLEFT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.InnerJoin:
 | 
			
		||||
                            sb.Append(" \r\nINNER JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case SelectTableInfoType.RightJoin:
 | 
			
		||||
                            sb.Append(" \r\nRIGHT JOIN ");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND (").Append(tb.Cascade).Append(")");
 | 
			
		||||
                    if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (_join.Length > 0) sb.Append(_join);
 | 
			
		||||
 | 
			
		||||
                sbnav.Append(_where);
 | 
			
		||||
                if (!string.IsNullOrEmpty(_tables[0].Cascade))
 | 
			
		||||
                    sbnav.Append(" AND (").Append(_tables[0].Cascade).Append(")");
 | 
			
		||||
 | 
			
		||||
                foreach (var tb in _tables)
 | 
			
		||||
                {
 | 
			
		||||
                    if (tb.Type == SelectTableInfoType.Parent) continue;
 | 
			
		||||
                    if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
 | 
			
		||||
                        sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
 | 
			
		||||
                }
 | 
			
		||||
                if (sbnav.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
 | 
			
		||||
                }
 | 
			
		||||
                if (string.IsNullOrEmpty(_groupby) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(_groupby);
 | 
			
		||||
                    if (string.IsNullOrEmpty(_having) == false)
 | 
			
		||||
                        sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append(_orderby);
 | 
			
		||||
                if (_limit > 0)
 | 
			
		||||
                    sb.Append(" \r\nlimit ").Append(_limit);
 | 
			
		||||
                if (_skip > 0)
 | 
			
		||||
                    sb.Append(" \r\noffset ").Append(_skip);
 | 
			
		||||
 | 
			
		||||
                sbnav.Clear();
 | 
			
		||||
                if (tbUnionsGt0) sb.Append(") ftb");
 | 
			
		||||
            }
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public OdbcPostgreSQLSelect(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 OdbcPostgreSQLSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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 OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); OdbcPostgreSQLSelect<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(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class 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 OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
    class OdbcPostgreSQLSelect<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 T1 : class 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 OdbcPostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
 | 
			
		||||
        public override string ToSql(string field = null) => OdbcPostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _whereCascadeExpression, _orm);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,149 @@
 | 
			
		||||
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.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public OdbcPostgreSQLUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
 | 
			
		||||
            : base(orm, commonUtils, commonExpression, dywhere)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteAffrows() => base.SplitExecuteAffrows(500, 3000);
 | 
			
		||||
        public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 3000);
 | 
			
		||||
        public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(500, 3000);
 | 
			
		||||
        public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 3000);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        protected override List<T1> RawExecuteUpdated()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var dbParms = _params.Concat(_paramsSource).ToArray();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
 | 
			
		||||
                ValidateVersionAndThrow(ret.Count);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        async protected override Task<List<T1>> RawExecuteUpdatedAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = this.ToSql();
 | 
			
		||||
            if (string.IsNullOrEmpty(sql)) return new List<T1>();
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            sb.Append(sql).Append(" RETURNING ");
 | 
			
		||||
 | 
			
		||||
            var colidx = 0;
 | 
			
		||||
            foreach (var col in _table.Columns.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (colidx > 0) sb.Append(", ");
 | 
			
		||||
                sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
 | 
			
		||||
                ++colidx;
 | 
			
		||||
            }
 | 
			
		||||
            sql = sb.ToString();
 | 
			
		||||
            var dbParms = _params.Concat(_paramsSource).ToArray();
 | 
			
		||||
            var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
 | 
			
		||||
            _orm.Aop.CurdBefore?.Invoke(this, before);
 | 
			
		||||
            var ret = new List<T1>();
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
 | 
			
		||||
                ValidateVersionAndThrow(ret.Count);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var after = new Aop.CurdAfterEventArgs(before, exception, ret);
 | 
			
		||||
                _orm.Aop.CurdAfter?.Invoke(this, after);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
 | 
			
		||||
        {
 | 
			
		||||
            if (_table.Primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append("(");
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in _table.Primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) caseWhen.Append(" || ");
 | 
			
		||||
                caseWhen.Append(_commonUtils.QuoteReadColumn(pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::varchar");
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
            caseWhen.Append(")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
 | 
			
		||||
        {
 | 
			
		||||
            if (_table.Primarys.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            sb.Append("(");
 | 
			
		||||
            var pkidx = 0;
 | 
			
		||||
            foreach (var pk in _table.Primarys)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkidx > 0) sb.Append(" || ");
 | 
			
		||||
                sb.Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d))).Append("::varchar");
 | 
			
		||||
                ++pkidx;
 | 
			
		||||
            }
 | 
			
		||||
            sb.Append(")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col)
 | 
			
		||||
        {
 | 
			
		||||
            if (_noneParameter == false) return;
 | 
			
		||||
            var dbtype = _commonUtils.CodeFirst.GetDbInfo(col.Attribute.MapType)?.dbtype;
 | 
			
		||||
            if (dbtype == null) return;
 | 
			
		||||
 | 
			
		||||
            sb.Append("::").Append(dbtype);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,71 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using SafeObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
    class OdbcPostgreSQLAdo : FreeSql.Internal.CommonProvider.AdoProvider
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLAdo() : base(DataType.PostgreSQL) { }
 | 
			
		||||
        public OdbcPostgreSQLAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.PostgreSQL)
 | 
			
		||||
        {
 | 
			
		||||
            base._util = util;
 | 
			
		||||
            if (!string.IsNullOrEmpty(masterConnectionString))
 | 
			
		||||
                MasterPool = new OdbcPostgreSQLConnectionPool("主库", masterConnectionString, null, null);
 | 
			
		||||
            if (slaveConnectionStrings != null)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var slaveConnectionString in slaveConnectionStrings)
 | 
			
		||||
                {
 | 
			
		||||
                    var slavePool = new OdbcPostgreSQLConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
 | 
			
		||||
                    SlavePools.Add(slavePool);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static DateTime dt1970 = new DateTime(1970, 1, 1);
 | 
			
		||||
        public override object AddslashesProcessParam(object param, Type mapType)
 | 
			
		||||
        {
 | 
			
		||||
            if (param == null) return "NULL";
 | 
			
		||||
            if (mapType != null && mapType != param.GetType())
 | 
			
		||||
                param = Utils.GetDataReaderValue(mapType, param);
 | 
			
		||||
            if (param is bool || param is bool?)
 | 
			
		||||
                return (bool)param ? "'t'" : "'f'";
 | 
			
		||||
            else if (param is string || param is char)
 | 
			
		||||
                return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
            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("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
 | 
			
		||||
            else if (param is TimeSpan || param is TimeSpan?)
 | 
			
		||||
                return ((TimeSpan)param).Ticks / 10;
 | 
			
		||||
            else if (param is IEnumerable)
 | 
			
		||||
            {
 | 
			
		||||
                var sb = new StringBuilder();
 | 
			
		||||
                var ie = param as IEnumerable;
 | 
			
		||||
                foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
 | 
			
		||||
                return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
 | 
			
		||||
            }
 | 
			
		||||
            return string.Concat("'", param.ToString().Replace("'", "''"), "'");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DbCommand CreateCommand()
 | 
			
		||||
        {
 | 
			
		||||
            return new OdbcCommand();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            (pool as OdbcPostgreSQLConnectionPool).Return(conn, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,241 @@
 | 
			
		||||
using SafeObjectPool;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLConnectionPool : ObjectPool<DbConnection>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        internal Action availableHandler;
 | 
			
		||||
        internal Action unavailableHandler;
 | 
			
		||||
 | 
			
		||||
        public OdbcPostgreSQLConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null)
 | 
			
		||||
        {
 | 
			
		||||
            var policy = new OdbcPostgreSQLConnectionPoolPolicy
 | 
			
		||||
            {
 | 
			
		||||
                _pool = this,
 | 
			
		||||
                Name = name
 | 
			
		||||
            };
 | 
			
		||||
            this.Policy = policy;
 | 
			
		||||
            policy.ConnectionString = connectionString;
 | 
			
		||||
 | 
			
		||||
            this.availableHandler = availableHandler;
 | 
			
		||||
            this.unavailableHandler = unavailableHandler;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false)
 | 
			
		||||
        {
 | 
			
		||||
            if (exception != null && exception is OdbcException)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (exception is System.IO.IOException)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    base.SetUnavailable(exception);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else if (obj.Value.Ping() == false)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    base.SetUnavailable(exception);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            base.Return(obj, isRecreate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLConnectionPoolPolicy : IPolicy<DbConnection>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        internal OdbcPostgreSQLConnectionPool _pool;
 | 
			
		||||
        public string Name { get; set; } = "PostgreSQL NpgsqlConnection 对象池";
 | 
			
		||||
        public int PoolSize { get; set; } = 50;
 | 
			
		||||
        public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
 | 
			
		||||
        public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
 | 
			
		||||
        public int AsyncGetCapacity { get; set; } = 10000;
 | 
			
		||||
        public bool IsThrowGetTimeoutException { get; set; } = true;
 | 
			
		||||
        public int CheckAvailableInterval { get; set; } = 5;
 | 
			
		||||
 | 
			
		||||
        static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
        private string _connectionString;
 | 
			
		||||
        public string ConnectionString
 | 
			
		||||
        {
 | 
			
		||||
            get => _connectionString;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                _connectionString = value ?? "";
 | 
			
		||||
 | 
			
		||||
                var pattern = @"Max(imum)?\s*pool\s*size\s*=\s*(\d+)";
 | 
			
		||||
                Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
 | 
			
		||||
                if (m.Success == false || int.TryParse(m.Groups[2].Value, out var poolsize) == false || poolsize <= 0) poolsize = 50;
 | 
			
		||||
                var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
 | 
			
		||||
                PoolSize = poolsize + connStrIncr;
 | 
			
		||||
                _connectionString = m.Success ?
 | 
			
		||||
                    Regex.Replace(_connectionString, pattern, $"Maximum pool size={PoolSize}", RegexOptions.IgnoreCase) :
 | 
			
		||||
                    $"{_connectionString};Maximum pool size={PoolSize}";
 | 
			
		||||
 | 
			
		||||
                pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
 | 
			
		||||
                m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
 | 
			
		||||
                if (m.Success)
 | 
			
		||||
                {
 | 
			
		||||
                    IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
 | 
			
		||||
                    _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var minPoolSize = 0;
 | 
			
		||||
                pattern = @"Min(imum)?\s*pool\s*size\s*=\s*(\d+)";
 | 
			
		||||
                m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
 | 
			
		||||
                if (m.Success)
 | 
			
		||||
                {
 | 
			
		||||
                    minPoolSize = int.Parse(m.Groups[2].Value);
 | 
			
		||||
                    _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool OnCheckAvailable(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
 | 
			
		||||
            return obj.Value.Ping(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DbConnection OnCreate()
 | 
			
		||||
        {
 | 
			
		||||
            var conn = new OdbcConnection(_connectionString);
 | 
			
		||||
            return conn;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnDestroy(DbConnection obj)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj.State != ConnectionState.Closed) obj.Close();
 | 
			
		||||
            obj.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnGet(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (_pool.IsAvailable)
 | 
			
		||||
            {
 | 
			
		||||
                if (obj.Value == null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
 | 
			
		||||
                        throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        obj.Value.Open();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_pool.SetUnavailable(ex) == true)
 | 
			
		||||
                            throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async public Task OnGetAsync(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (_pool.IsAvailable)
 | 
			
		||||
            {
 | 
			
		||||
                if (obj.Value == null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
 | 
			
		||||
                        throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (await obj.Value.PingAsync()) == false)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        await obj.Value.OpenAsync();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_pool.SetUnavailable(ex) == true)
 | 
			
		||||
                            throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnGetTimeout()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnReturn(Object<DbConnection> obj)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnAvailable()
 | 
			
		||||
        {
 | 
			
		||||
            _pool.availableHandler?.Invoke();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnUnavailable()
 | 
			
		||||
        {
 | 
			
		||||
            _pool.unavailableHandler?.Invoke();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class DbConnectionExtensions
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        static DbCommand PingCommand(DbConnection conn)
 | 
			
		||||
        {
 | 
			
		||||
            var cmd = conn.CreateCommand();
 | 
			
		||||
            cmd.CommandTimeout = 5;
 | 
			
		||||
            cmd.CommandText = "select 1";
 | 
			
		||||
            return cmd;
 | 
			
		||||
        }
 | 
			
		||||
        public static bool Ping(this DbConnection that, bool isThrow = false)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                PingCommand(that).ExecuteNonQuery();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
 | 
			
		||||
                if (isThrow) throw;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await PingCommand(that).ExecuteNonQueryAsync();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
 | 
			
		||||
                if (isThrow) throw;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,342 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLCodeFirst : Internal.CommonProvider.CodeFirstProvider
 | 
			
		||||
    {
 | 
			
		||||
        public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
 | 
			
		||||
        public OdbcPostgreSQLCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
 | 
			
		||||
 | 
			
		||||
        static object _dicCsToDbLock = new object();
 | 
			
		||||
        static Dictionary<string, (OdbcType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (OdbcType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
 | 
			
		||||
 | 
			
		||||
                { typeof(sbyte).FullName,  (OdbcType.SmallInt, "int2","int2 NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName,  (OdbcType.SmallInt, "int2", "int2", false, true, null) },
 | 
			
		||||
                { typeof(short).FullName,  (OdbcType.SmallInt, "int2","int2 NOT NULL", false, false, 0) },{ typeof(short?).FullName,  (OdbcType.SmallInt, "int2", "int2", false, true, null) },
 | 
			
		||||
                { typeof(int).FullName,  (OdbcType.Int, "int4","int4 NOT NULL", false, false, 0) },{ typeof(int?).FullName,  (OdbcType.Int, "int4", "int4", false, true, null) },
 | 
			
		||||
                { typeof(long).FullName,  (OdbcType.BigInt, "int8","int8 NOT NULL", false, false, 0) },{ typeof(long?).FullName,  (OdbcType.BigInt, "int8", "int8", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(byte).FullName,  (OdbcType.SmallInt, "int2","int2 NOT NULL", false, false, 0) },{ typeof(byte?).FullName,  (OdbcType.SmallInt, "int2", "int2", false, true, null) },
 | 
			
		||||
                { typeof(ushort).FullName,  (OdbcType.Int, "int4","int4 NOT NULL", false, false, 0) },{ typeof(ushort?).FullName,  (OdbcType.Int, "int4", "int4", false, true, null) },
 | 
			
		||||
                { typeof(uint).FullName,  (OdbcType.BigInt, "int8","int8 NOT NULL", false, false, 0) },{ typeof(uint?).FullName,  (OdbcType.BigInt, "int8", "int8", false, true, null) },
 | 
			
		||||
                { typeof(ulong).FullName,  (OdbcType.Numeric, "numeric","numeric(20,0) NOT NULL", false, false, 0) },{ typeof(ulong?).FullName,  (OdbcType.Numeric, "numeric", "numeric(20,0)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(float).FullName,  (OdbcType.Real, "float4","float4 NOT NULL", false, false, 0) },{ typeof(float?).FullName,  (OdbcType.Real, "float4", "float4", false, true, null) },
 | 
			
		||||
                { typeof(double).FullName,  (OdbcType.Double, "float8","float8 NOT NULL", false, false, 0) },{ typeof(double?).FullName,  (OdbcType.Double, "float8", "float8", false, true, null) },
 | 
			
		||||
                { typeof(decimal).FullName,  (OdbcType.Numeric, "numeric", "numeric(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName,  (OdbcType.Numeric, "numeric", "numeric(10,2)", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(string).FullName,  (OdbcType.VarChar, "varchar", "varchar(255)", false, null, "") },
 | 
			
		||||
 | 
			
		||||
                { typeof(TimeSpan).FullName,  (OdbcType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName,  (OdbcType.Time, "time", "time",false, true, null) },
 | 
			
		||||
                { typeof(DateTime).FullName,  (OdbcType.DateTime, "timestamp", "timestamp NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName,  (OdbcType.DateTime, "timestamp", "timestamp", false, true, null) },
 | 
			
		||||
 | 
			
		||||
                { typeof(bool).FullName,  (OdbcType.Bit, "bool","bool NOT NULL", null, false, false) },{ typeof(bool?).FullName,  (OdbcType.Bit, "bool","bool", null, true, null) },
 | 
			
		||||
                { typeof(Byte[]).FullName,  (OdbcType.VarBinary, "bytea", "bytea", false, null, new byte[0]) },
 | 
			
		||||
 | 
			
		||||
                { typeof(Guid).FullName,  (OdbcType.UniqueIdentifier, "uuid", "uuid NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName,  (OdbcType.UniqueIdentifier, "uuid", "uuid", false, true, null) },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public override (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type)
 | 
			
		||||
        {
 | 
			
		||||
            var isarray = type.FullName != "System.Byte[]" && type.IsArray;
 | 
			
		||||
            var elementType = isarray ? type.GetElementType() : type;
 | 
			
		||||
            var info = GetDbInfoNoneArray(elementType);
 | 
			
		||||
            if (info == null) return null;
 | 
			
		||||
            return ((int)info.Value.type, info.Value.dbtype, info.Value.dbtypeFull, info.Value.isnullable, info.Value.defaultValue);
 | 
			
		||||
        }
 | 
			
		||||
        (OdbcType type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfoNoneArray(Type type)
 | 
			
		||||
        {
 | 
			
		||||
            if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (OdbcType, string, string, bool?, object)?((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() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
 | 
			
		||||
            if (enumType != null)
 | 
			
		||||
            {
 | 
			
		||||
                var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
 | 
			
		||||
                    (OdbcType.Int, "int8", $"int8{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
 | 
			
		||||
                    (OdbcType.BigInt, "int4", $"int4{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
 | 
			
		||||
                if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    lock (_dicCsToDbLock)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_dicCsToDb.ContainsKey(type.FullName) == false)
 | 
			
		||||
                            _dicCsToDb.Add(type.FullName, newItem);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return (newItem.Item1, newItem.Item2, newItem.Item3, newItem.Item5, newItem.Item6);
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string GetComparisonDDLStatements(params Type[] entityTypes)
 | 
			
		||||
        {
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
            var seqcols = new List<(ColumnInfo, string[], bool)>(); //序列
 | 
			
		||||
 | 
			
		||||
            foreach (var entityType in entityTypes)
 | 
			
		||||
            {
 | 
			
		||||
                if (sb.Length > 0) sb.Append("\r\n");
 | 
			
		||||
                var tb = _commonUtils.GetTableByEntity(entityType);
 | 
			
		||||
                if (tb == null) throw new Exception($"类型 {entityType.FullName} 不可迁移");
 | 
			
		||||
                if (tb.Columns.Any() == false) throw new Exception($"类型 {entityType.FullName} 不可迁移,可迁移属性0个");
 | 
			
		||||
                var tbname = tb.DbName.Split(new[] { '.' }, 2);
 | 
			
		||||
                if (tbname?.Length == 1) tbname = new[] { "public", tbname[0] };
 | 
			
		||||
 | 
			
		||||
                var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
 | 
			
		||||
                if (tboldname?.Length == 1) tboldname = new[] { "public", tboldname[0] };
 | 
			
		||||
 | 
			
		||||
                if (string.Compare(tbname[0], "public", true) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from pg_namespace where nspname={0}", tbname[0])) == null) //创建模式
 | 
			
		||||
                    sb.Append("CREATE SCHEMA IF NOT EXISTS ").Append(tbname[0]).Append(";\r\n");
 | 
			
		||||
 | 
			
		||||
                var sbalter = new StringBuilder();
 | 
			
		||||
                var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
 | 
			
		||||
                if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tbname)) == null)
 | 
			
		||||
                { //表不存在
 | 
			
		||||
                    if (tboldname != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tboldname)) == null)
 | 
			
		||||
                            //旧表不存在
 | 
			
		||||
                            tboldname = null;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (tboldname == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        //创建表
 | 
			
		||||
                        sb.Append("CREATE TABLE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).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((tbcol, tbname, true));
 | 
			
		||||
                        }
 | 
			
		||||
                        if (tb.Primarys.Any())
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\n  CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pkey PRIMARY KEY (");
 | 
			
		||||
                            foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                            sb.Remove(sb.Length - 2, 2).Append("),");
 | 
			
		||||
                        }
 | 
			
		||||
                        foreach (var uk in tb.Uniques)
 | 
			
		||||
                        {
 | 
			
		||||
                            sb.Append(" \r\n  CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
 | 
			
		||||
                            foreach (var tbcol in uk.Value) 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) WITH (OIDS=FALSE);\r\n");
 | 
			
		||||
                        //备注
 | 
			
		||||
                        foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (string.IsNullOrEmpty(tbcol.Comment) == false)
 | 
			
		||||
                                sb.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n");
 | 
			
		||||
                        }
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    //如果新表,旧表在一个数据库和模式下,直接修改表名
 | 
			
		||||
                    if (string.Compare(tbname[0], tboldname[0], true) == 0)
 | 
			
		||||
                        sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append(";\r\n");
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //如果新表,旧表不在一起,创建新表,导入数据,删除旧表
 | 
			
		||||
                        istmpatler = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    tboldname = null; //如果新表已经存在,不走改表名逻辑
 | 
			
		||||
 | 
			
		||||
                //对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
 | 
			
		||||
                var sql = _commonUtils.FormatSql(@"
 | 
			
		||||
select
 | 
			
		||||
a.attname,
 | 
			
		||||
t.typname,
 | 
			
		||||
case when a.atttypmod > 0 and a.atttypmod < 32767 then a.atttypmod - 4 else a.attlen end len,
 | 
			
		||||
case when t.typelem > 0 and t.typinput::varchar = 'array_in' then t2.typname else t.typname end,
 | 
			
		||||
case when a.attnotnull then '0' else '1' end as is_nullable,
 | 
			
		||||
e.adsrc,
 | 
			
		||||
a.attndims,
 | 
			
		||||
d.description as comment
 | 
			
		||||
from pg_class c
 | 
			
		||||
inner join pg_attribute a on a.attnum > 0 and a.attrelid = c.oid
 | 
			
		||||
inner join pg_type t on t.oid = a.atttypid
 | 
			
		||||
left join pg_type t2 on t2.oid = t.typelem
 | 
			
		||||
left join pg_description d on d.objoid = a.attrelid and d.objsubid = a.attnum
 | 
			
		||||
left join pg_attrdef e on e.adrelid = a.attrelid and e.adnum = a.attnum
 | 
			
		||||
inner join pg_namespace ns on ns.oid = c.relnamespace
 | 
			
		||||
inner join pg_namespace ns2 on ns2.oid = t.typnamespace
 | 
			
		||||
where ns.nspname = {0} and c.relname = {1}", tboldname ?? tbname);
 | 
			
		||||
                var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a =>
 | 
			
		||||
                {
 | 
			
		||||
                    var attndims = int.Parse(string.Concat(a[6]));
 | 
			
		||||
                    var type = string.Concat(a[1]);
 | 
			
		||||
                    var sqlType = string.Concat(a[3]);
 | 
			
		||||
                    var max_length = long.Parse(string.Concat(a[2]));
 | 
			
		||||
                    switch (sqlType.ToLower())
 | 
			
		||||
                    {
 | 
			
		||||
                        case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break;
 | 
			
		||||
                        default: max_length *= 8; break;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (type.StartsWith("_"))
 | 
			
		||||
                    {
 | 
			
		||||
                        type = type.Substring(1);
 | 
			
		||||
                        if (attndims == 0) attndims++;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (sqlType.StartsWith("_")) sqlType = sqlType.Substring(1);
 | 
			
		||||
                    return new
 | 
			
		||||
                    {
 | 
			
		||||
                        column = string.Concat(a[0]),
 | 
			
		||||
                        sqlType = string.Concat(sqlType),
 | 
			
		||||
                        max_length = long.Parse(string.Concat(a[2])),
 | 
			
		||||
                        is_nullable = string.Concat(a[4]) == "1",
 | 
			
		||||
                        is_identity = string.Concat(a[5]).StartsWith(@"nextval('") && string.Concat(a[5]).EndsWith(@"'::regclass)"),
 | 
			
		||||
                        attndims,
 | 
			
		||||
                        comment = string.Concat(a[7])
 | 
			
		||||
                    };
 | 
			
		||||
                }, StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
 | 
			
		||||
                if (istmpatler == false)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    {
 | 
			
		||||
                        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 ||
 | 
			
		||||
                                tbcol.Attribute.DbType.Contains("[]") != (tbstructcol.attndims > 0))
 | 
			
		||||
                                sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TYPE ").Append(tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n");
 | 
			
		||||
                            if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (tbcol.Attribute.IsNullable != true || tbcol.Attribute.IsNullable == true && tbcol.Attribute.IsPrimary == false)
 | 
			
		||||
                                    sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable == true ? "DROP" : "SET").Append(" NOT NULL;\r\n");
 | 
			
		||||
                            }
 | 
			
		||||
                            if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
 | 
			
		||||
                                seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity == true));
 | 
			
		||||
                            if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0)
 | 
			
		||||
                                //修改列名
 | 
			
		||||
                                sbalter.Append("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 (isCommentChanged)
 | 
			
		||||
                                sbalter.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n");
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        //添加列
 | 
			
		||||
                        sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n");
 | 
			
		||||
                        sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" = ").Append(_commonUtils.FormatSql("{0};\r\n", tbcol.Attribute.DbDefautValue));
 | 
			
		||||
                        if (tbcol.Attribute.IsNullable == false) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" SET NOT NULL;\r\n");
 | 
			
		||||
                        if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity == true));
 | 
			
		||||
                        if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n");
 | 
			
		||||
                    }
 | 
			
		||||
                    var dsuksql = _commonUtils.FormatSql(@"
 | 
			
		||||
select
 | 
			
		||||
c.attname,
 | 
			
		||||
b.relname
 | 
			
		||||
from pg_index a
 | 
			
		||||
inner join pg_class b on b.oid = a.indexrelid
 | 
			
		||||
inner join pg_attribute c on c.attnum > 0 and c.attrelid = b.oid
 | 
			
		||||
inner join pg_namespace ns on ns.oid = b.relnamespace
 | 
			
		||||
inner join pg_class d on d.oid = a.indrelid
 | 
			
		||||
where ns.nspname in ({0}) and d.relname in ({1}) and a.indisunique = 't'", tboldname ?? tbname);
 | 
			
		||||
                    var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]) });
 | 
			
		||||
                    foreach (var uk in tb.Uniques)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
 | 
			
		||||
                        var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
 | 
			
		||||
                        if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (dsukfind1.Any()) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(";\r\n");
 | 
			
		||||
                            sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
 | 
			
		||||
                            foreach (var tbcol in uk.Value) sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                            sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (istmpatler == false)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(sbalter);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                var oldpk = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@"select pg_constraint.conname as pk_name from pg_constraint
 | 
			
		||||
inner join pg_class on pg_constraint.conrelid = pg_class.oid
 | 
			
		||||
inner join pg_namespace on pg_namespace.oid = pg_class.relnamespace
 | 
			
		||||
where pg_namespace.nspname={0} and pg_class.relname={1} and pg_constraint.contype='p'
 | 
			
		||||
", tbname))?.ToString();
 | 
			
		||||
                if (string.IsNullOrEmpty(oldpk) == false)
 | 
			
		||||
                    sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(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]}.FreeSqlTmp_{tbname[1]}");
 | 
			
		||||
                //创建临时表
 | 
			
		||||
                sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( ");
 | 
			
		||||
                foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\n  ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
 | 
			
		||||
                    if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, true));
 | 
			
		||||
                }
 | 
			
		||||
                if (tb.Primarys.Any())
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\n  CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pkey PRIMARY KEY (");
 | 
			
		||||
                    foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                    sb.Remove(sb.Length - 2, 2).Append("),");
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var uk in tb.Uniques)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append(" \r\n  CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
 | 
			
		||||
                    foreach (var tbcol in uk.Value) 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) WITH (OIDS=FALSE);\r\n");
 | 
			
		||||
                //备注
 | 
			
		||||
                foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.IsNullOrEmpty(tbcol.Comment) == false)
 | 
			
		||||
                        sb.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FreeSqlTmp_{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n");
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
 | 
			
		||||
                foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                    sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
 | 
			
		||||
                sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
 | 
			
		||||
                foreach (var tbcol in tb.ColumnsByPosition)
 | 
			
		||||
                {
 | 
			
		||||
                    var insertvalue = "NULL";
 | 
			
		||||
                    if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
 | 
			
		||||
                        string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
 | 
			
		||||
                    {
 | 
			
		||||
                        insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
 | 
			
		||||
                        if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
 | 
			
		||||
                            insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
 | 
			
		||||
                        if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
 | 
			
		||||
                            insertvalue = $"coalesce({insertvalue},{_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue)})";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (tbcol.Attribute.IsNullable == false)
 | 
			
		||||
                        insertvalue = _commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue);
 | 
			
		||||
                    sb.Append(insertvalue).Append(", ");
 | 
			
		||||
                }
 | 
			
		||||
                sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n");
 | 
			
		||||
                sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
 | 
			
		||||
                sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[1])).Append(";\r\n");
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var seqcol in seqcols)
 | 
			
		||||
            {
 | 
			
		||||
                var tbname = seqcol.Item2;
 | 
			
		||||
                var seqname = Utils.GetCsName($"{tbname[0]}.{tbname[1]}_{seqcol.Item1.Attribute.Name}_sequence_name");
 | 
			
		||||
                var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
 | 
			
		||||
                var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name);
 | 
			
		||||
                sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT null;\r\n");
 | 
			
		||||
                sb.Append("DROP SEQUENCE IF EXISTS ").Append(seqname).Append(";\r\n");
 | 
			
		||||
                if (seqcol.Item3)
 | 
			
		||||
                {
 | 
			
		||||
                    sb.Append("CREATE SEQUENCE ").Append(seqname).Append(";\r\n");
 | 
			
		||||
                    sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT nextval('").Append(seqname).Append("'::regclass);\r\n");
 | 
			
		||||
                    sb.Append(" SELECT case when max(").Append(colname2).Append(") is null then 0 else setval('").Append(seqname).Append("', max(").Append(colname2).Append(")) end FROM ").Append(tbname2).Append(";\r\n");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return sb.Length == 0 ? null : sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,445 @@
 | 
			
		||||
using FreeSql.DatabaseModel;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
    class OdbcPostgreSQLDbFirst : IDbFirst
 | 
			
		||||
    {
 | 
			
		||||
        IFreeSql _orm;
 | 
			
		||||
        protected CommonUtils _commonUtils;
 | 
			
		||||
        protected CommonExpression _commonExpression;
 | 
			
		||||
        public OdbcPostgreSQLDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
 | 
			
		||||
        {
 | 
			
		||||
            _orm = orm;
 | 
			
		||||
            _commonUtils = commonUtils;
 | 
			
		||||
            _commonExpression = commonExpression;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetDbType(DbColumnInfo column) => (int)GetOdbcType(column);
 | 
			
		||||
        OdbcType GetOdbcType(DbColumnInfo column)
 | 
			
		||||
        {
 | 
			
		||||
            var dbtype = column.DbTypeText;
 | 
			
		||||
            var isarray = dbtype.EndsWith("[]");
 | 
			
		||||
            if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2);
 | 
			
		||||
            OdbcType ret = OdbcType.VarChar;
 | 
			
		||||
            switch (dbtype.ToLower().TrimStart('_'))
 | 
			
		||||
            {
 | 
			
		||||
                case "int2": ret = OdbcType.SmallInt; break;
 | 
			
		||||
                case "int4": ret = OdbcType.Int; break;
 | 
			
		||||
                case "int8": ret = OdbcType.BigInt; break;
 | 
			
		||||
                case "numeric": ret = OdbcType.Numeric; break;
 | 
			
		||||
                case "float4": ret = OdbcType.Real; break;
 | 
			
		||||
                case "float8": ret = OdbcType.Double; break;
 | 
			
		||||
                case "money": ret = OdbcType.Numeric; break;
 | 
			
		||||
 | 
			
		||||
                case "bpchar": ret = OdbcType.Char; break;
 | 
			
		||||
                case "varchar": ret = OdbcType.VarChar; break;
 | 
			
		||||
                case "text": ret = OdbcType.Text; break;
 | 
			
		||||
 | 
			
		||||
                case "timestamp": ret = OdbcType.DateTime; break;
 | 
			
		||||
                case "timestamptz": ret = OdbcType.DateTime; break;
 | 
			
		||||
                case "date": ret = OdbcType.Date; break;
 | 
			
		||||
                case "time": ret = OdbcType.Time; break;
 | 
			
		||||
                case "timetz": ret = OdbcType.Time; break;
 | 
			
		||||
                case "interval": ret = OdbcType.Time; break;
 | 
			
		||||
 | 
			
		||||
                case "bool": ret = OdbcType.Bit; break;
 | 
			
		||||
                case "bytea": ret = OdbcType.VarBinary; break;
 | 
			
		||||
                case "bit": ret = OdbcType.Bit; break;
 | 
			
		||||
                case "varbit": ret = OdbcType.VarBinary; break;
 | 
			
		||||
 | 
			
		||||
                case "json": ret = OdbcType.VarChar; break;
 | 
			
		||||
                case "jsonb": ret = OdbcType.VarChar; break;
 | 
			
		||||
                case "uuid": ret = OdbcType.UniqueIdentifier; break;
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static readonly Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)> _dicDbToCs = new Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)>() {
 | 
			
		||||
                { (int)OdbcType.SmallInt, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
 | 
			
		||||
                { (int)OdbcType.Int, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
 | 
			
		||||
                { (int)OdbcType.BigInt, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
 | 
			
		||||
                { (int)OdbcType.Numeric, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
 | 
			
		||||
                { (int)OdbcType.Real, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
 | 
			
		||||
                { (int)OdbcType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
 | 
			
		||||
 | 
			
		||||
                { (int)OdbcType.Char, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
                { (int)OdbcType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
                { (int)OdbcType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
 | 
			
		||||
 | 
			
		||||
                { (int)OdbcType.DateTime,  ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
                { (int)OdbcType.Date,  ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
 | 
			
		||||
                { (int)OdbcType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
 | 
			
		||||
 | 
			
		||||
                { (int)OdbcType.Bit, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
 | 
			
		||||
                { (int)OdbcType.VarBinary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
 | 
			
		||||
 | 
			
		||||
                { (int)OdbcType.UniqueIdentifier, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
 | 
			
		||||
        public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
 | 
			
		||||
        public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
 | 
			
		||||
        public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
 | 
			
		||||
        public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
 | 
			
		||||
        public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
 | 
			
		||||
        public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
 | 
			
		||||
 | 
			
		||||
        public List<string> GetDatabases()
 | 
			
		||||
        {
 | 
			
		||||
            var sql = @" select datname from pg_database where datname not in ('template1', 'template0')";
 | 
			
		||||
            var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
            return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public List<DbTableInfo> GetTablesByDatabase(params string[] database)
 | 
			
		||||
        {
 | 
			
		||||
            var olddatabase = "";
 | 
			
		||||
            using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
 | 
			
		||||
            {
 | 
			
		||||
                olddatabase = conn.Value.Database;
 | 
			
		||||
            }
 | 
			
		||||
            var dbs = database == null || database.Any() == false ? new[] { olddatabase } : database;
 | 
			
		||||
            var tables = new List<DbTableInfo>();
 | 
			
		||||
 | 
			
		||||
            foreach (var db in dbs)
 | 
			
		||||
            {
 | 
			
		||||
                if (string.IsNullOrEmpty(db) || string.Compare(db, olddatabase, true) != 0) continue;
 | 
			
		||||
 | 
			
		||||
                var loc1 = new List<DbTableInfo>();
 | 
			
		||||
                var loc2 = new Dictionary<string, DbTableInfo>();
 | 
			
		||||
                var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
 | 
			
		||||
 | 
			
		||||
                var sql = $@"
 | 
			
		||||
select
 | 
			
		||||
b.nspname || '.' || a.tablename,
 | 
			
		||||
a.schemaname,
 | 
			
		||||
a.tablename ,
 | 
			
		||||
d.description,
 | 
			
		||||
'TABLE'
 | 
			
		||||
from pg_tables a
 | 
			
		||||
inner join pg_namespace b on b.nspname = a.schemaname
 | 
			
		||||
inner join pg_class c on c.relnamespace = b.oid and c.relname = a.tablename
 | 
			
		||||
left join pg_description d on d.objoid = c.oid and objsubid = 0
 | 
			
		||||
where a.schemaname not in ('pg_catalog', 'information_schema', 'topology')
 | 
			
		||||
and b.nspname || '.' || a.tablename not in ('public.spatial_ref_sys')
 | 
			
		||||
 | 
			
		||||
union all
 | 
			
		||||
 | 
			
		||||
select
 | 
			
		||||
b.nspname || '.' || a.relname,
 | 
			
		||||
b.nspname,
 | 
			
		||||
a.relname,
 | 
			
		||||
d.description,
 | 
			
		||||
'VIEW'
 | 
			
		||||
from pg_class a
 | 
			
		||||
inner join pg_namespace b on b.oid = a.relnamespace
 | 
			
		||||
left join pg_description d on d.objoid = a.oid and objsubid = 0
 | 
			
		||||
where b.nspname not in ('pg_catalog', 'information_schema') and a.relkind in ('m','v') 
 | 
			
		||||
and b.nspname || '.' || a.relname not in ('public.geography_columns','public.geometry_columns','public.raster_columns','public.raster_overviews')
 | 
			
		||||
";
 | 
			
		||||
                var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var loc6 = new List<string>();
 | 
			
		||||
                var loc66 = new List<string>();
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    var object_id = string.Concat(row[0]);
 | 
			
		||||
                    var owner = string.Concat(row[1]);
 | 
			
		||||
                    var table = string.Concat(row[2]);
 | 
			
		||||
                    var comment = string.Concat(row[3]);
 | 
			
		||||
                    Enum.TryParse<DbTableType>(string.Concat(row[4]), out var type);
 | 
			
		||||
                    loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Comment = comment, Type = type });
 | 
			
		||||
                    loc3.Add(object_id, new Dictionary<string, DbColumnInfo>());
 | 
			
		||||
                    switch (type)
 | 
			
		||||
                    {
 | 
			
		||||
                        case DbTableType.VIEW:
 | 
			
		||||
                        case DbTableType.TABLE:
 | 
			
		||||
                            loc6.Add(object_id);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case DbTableType.StoreProcedure:
 | 
			
		||||
                            loc66.Add(object_id);
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (loc6.Count == 0) return loc1;
 | 
			
		||||
                string loc8 = "'" + string.Join("','", loc6.ToArray()) + "'";
 | 
			
		||||
                string loc88 = "'" + string.Join("','", loc66.ToArray()) + "'";
 | 
			
		||||
 | 
			
		||||
                sql = $@"
 | 
			
		||||
select
 | 
			
		||||
ns.nspname || '.' || c.relname as id, 
 | 
			
		||||
a.attname,
 | 
			
		||||
t.typname,
 | 
			
		||||
case when a.atttypmod > 0 and a.atttypmod < 32767 then a.atttypmod - 4 else a.attlen end len,
 | 
			
		||||
case when t.typelem = 0 then t.typname else t2.typname end,
 | 
			
		||||
case when a.attnotnull then 0 else 1 end as is_nullable,
 | 
			
		||||
e.adsrc as is_identity,
 | 
			
		||||
--case when e.adsrc = 'nextval(''' || case when ns.nspname = 'public' then '' else ns.nspname || '.' end || c.relname || '_' || a.attname || '_seq''::regclass)' then 1 else 0 end as is_identity,
 | 
			
		||||
d.description as comment,
 | 
			
		||||
a.attndims,
 | 
			
		||||
case when t.typelem = 0 then t.typtype else t2.typtype end,
 | 
			
		||||
ns2.nspname,
 | 
			
		||||
a.attnum
 | 
			
		||||
from pg_class c
 | 
			
		||||
inner join pg_attribute a on a.attnum > 0 and a.attrelid = c.oid
 | 
			
		||||
inner join pg_type t on t.oid = a.atttypid
 | 
			
		||||
left join pg_type t2 on t2.oid = t.typelem
 | 
			
		||||
left join pg_description d on d.objoid = a.attrelid and d.objsubid = a.attnum
 | 
			
		||||
left join pg_attrdef e on e.adrelid = a.attrelid and e.adnum = a.attnum
 | 
			
		||||
inner join pg_namespace ns on ns.oid = c.relnamespace
 | 
			
		||||
inner join pg_namespace ns2 on ns2.oid = t.typnamespace
 | 
			
		||||
where ns.nspname || '.' || c.relname in ({loc8})";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    var object_id = string.Concat(row[0]);
 | 
			
		||||
                    var column = string.Concat(row[1]);
 | 
			
		||||
                    var type = string.Concat(row[2]);
 | 
			
		||||
                    var max_length = int.Parse(string.Concat(row[3]));
 | 
			
		||||
                    var sqlType = string.Concat(row[4]);
 | 
			
		||||
                    var is_nullable = string.Concat(row[5]) == "1";
 | 
			
		||||
                    var is_identity = string.Concat(row[6]).StartsWith(@"nextval('") && string.Concat(row[6]).EndsWith(@"'::regclass)");
 | 
			
		||||
                    var comment = string.Concat(row[7]);
 | 
			
		||||
                    int attndims = int.Parse(string.Concat(row[8]));
 | 
			
		||||
                    string typtype = string.Concat(row[9]);
 | 
			
		||||
                    string owner = string.Concat(row[10]);
 | 
			
		||||
                    int attnum = int.Parse(string.Concat(row[11]));
 | 
			
		||||
                    switch (sqlType.ToLower())
 | 
			
		||||
                    {
 | 
			
		||||
                        case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break;
 | 
			
		||||
                        default: max_length *= 8; break;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (max_length <= 0) max_length = -1;
 | 
			
		||||
                    if (type.StartsWith("_"))
 | 
			
		||||
                    {
 | 
			
		||||
                        type = type.Substring(1);
 | 
			
		||||
                        if (attndims == 0) attndims++;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (sqlType.StartsWith("_")) sqlType = sqlType.Substring(1);
 | 
			
		||||
 | 
			
		||||
                    loc3[object_id].Add(column, new DbColumnInfo
 | 
			
		||||
                    {
 | 
			
		||||
                        Name = column,
 | 
			
		||||
                        MaxLength = max_length,
 | 
			
		||||
                        IsIdentity = is_identity,
 | 
			
		||||
                        IsNullable = is_nullable,
 | 
			
		||||
                        IsPrimary = false,
 | 
			
		||||
                        DbTypeText = type,
 | 
			
		||||
                        DbTypeTextFull = sqlType,
 | 
			
		||||
                        Table = loc2[object_id],
 | 
			
		||||
                        Coment = comment
 | 
			
		||||
                    });
 | 
			
		||||
                    loc3[object_id][column].DbType = this.GetDbType(loc3[object_id][column]);
 | 
			
		||||
                    loc3[object_id][column].CsType = this.GetCsTypeInfo(loc3[object_id][column]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sql = $@"
 | 
			
		||||
select
 | 
			
		||||
ns.nspname || '.' || d.relname as table_id, 
 | 
			
		||||
c.attname,
 | 
			
		||||
b.relname as index_id,
 | 
			
		||||
case when a.indisunique then 1 else 0 end IsUnique,
 | 
			
		||||
case when a.indisprimary then 1 else 0 end IsPrimary,
 | 
			
		||||
case when a.indisclustered then 0 else 1 end IsClustered,
 | 
			
		||||
0 IsDesc,
 | 
			
		||||
a.indkey::text,
 | 
			
		||||
c.attnum
 | 
			
		||||
from pg_index a
 | 
			
		||||
inner join pg_class b on b.oid = a.indexrelid
 | 
			
		||||
inner join pg_attribute c on c.attnum > 0 and c.attrelid = b.oid
 | 
			
		||||
inner join pg_namespace ns on ns.oid = b.relnamespace
 | 
			
		||||
inner join pg_class d on d.oid = a.indrelid
 | 
			
		||||
where ns.nspname || '.' || d.relname in ({loc8})
 | 
			
		||||
";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var indexColumns = new Dictionary<string, Dictionary<string, List<DbColumnInfo>>>();
 | 
			
		||||
                var uniqueColumns = new Dictionary<string, Dictionary<string, List<DbColumnInfo>>>();
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    var object_id = string.Concat(row[0]);
 | 
			
		||||
                    var column = string.Concat(row[1]);
 | 
			
		||||
                    var index_id = string.Concat(row[2]);
 | 
			
		||||
                    var is_unique = string.Concat(row[3]) == "1";
 | 
			
		||||
                    var is_primary_key = string.Concat(row[4]) == "1";
 | 
			
		||||
                    var is_clustered = string.Concat(row[5]) == "1";
 | 
			
		||||
                    var is_desc = int.Parse(string.Concat(row[6]));
 | 
			
		||||
                    var inkey = string.Concat(row[7]).Split(' ');
 | 
			
		||||
                    var attnum = int.Parse(string.Concat(row[8]));
 | 
			
		||||
                    attnum = int.Parse(inkey[attnum - 1]);
 | 
			
		||||
                    foreach (string tc in loc3[object_id].Keys)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (loc3[object_id][tc].DbTypeText.EndsWith("[]"))
 | 
			
		||||
                        {
 | 
			
		||||
                            column = tc;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (loc3.ContainsKey(object_id) == false || loc3[object_id].ContainsKey(column) == false) continue;
 | 
			
		||||
                    var loc9 = loc3[object_id][column];
 | 
			
		||||
                    if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
 | 
			
		||||
 | 
			
		||||
                    Dictionary<string, List<DbColumnInfo>> loc10 = null;
 | 
			
		||||
                    List<DbColumnInfo> loc11 = null;
 | 
			
		||||
                    if (!indexColumns.TryGetValue(object_id, out loc10))
 | 
			
		||||
                        indexColumns.Add(object_id, loc10 = new Dictionary<string, List<DbColumnInfo>>());
 | 
			
		||||
                    if (!loc10.TryGetValue(index_id, out loc11))
 | 
			
		||||
                        loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
 | 
			
		||||
                    loc11.Add(loc9);
 | 
			
		||||
                    if (is_unique && !is_primary_key)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!uniqueColumns.TryGetValue(object_id, out loc10))
 | 
			
		||||
                            uniqueColumns.Add(object_id, loc10 = new Dictionary<string, List<DbColumnInfo>>());
 | 
			
		||||
                        if (!loc10.TryGetValue(index_id, out loc11))
 | 
			
		||||
                            loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
 | 
			
		||||
                        loc11.Add(loc9);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var object_id in indexColumns.Keys)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var column in indexColumns[object_id])
 | 
			
		||||
                        loc2[object_id].IndexesDict.Add(column.Key, column.Value);
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var object_id in uniqueColumns.Keys)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var column in uniqueColumns[object_id])
 | 
			
		||||
                    {
 | 
			
		||||
                        column.Value.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
 | 
			
		||||
                        loc2[object_id].UniquesDict.Add(column.Key, column.Value);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sql = $@"
 | 
			
		||||
select
 | 
			
		||||
ns.nspname || '.' || b.relname as table_id, 
 | 
			
		||||
array(select attname from pg_attribute where attrelid = a.conrelid and attnum = any(a.conkey)) as column_name,
 | 
			
		||||
a.conname as FKId,
 | 
			
		||||
ns2.nspname || '.' || c.relname as ref_table_id, 
 | 
			
		||||
1 as IsForeignKey,
 | 
			
		||||
array(select attname from pg_attribute where attrelid = a.confrelid and attnum = any(a.confkey)) as ref_column,
 | 
			
		||||
null ref_sln,
 | 
			
		||||
null ref_table
 | 
			
		||||
from  pg_constraint a
 | 
			
		||||
inner join pg_class b on b.oid = a.conrelid
 | 
			
		||||
inner join pg_class c on c.oid = a.confrelid
 | 
			
		||||
inner join pg_namespace ns on ns.oid = b.relnamespace
 | 
			
		||||
inner join pg_namespace ns2 on ns2.oid = c.relnamespace
 | 
			
		||||
where ns.nspname || '.' || b.relname in ({loc8})
 | 
			
		||||
";
 | 
			
		||||
                ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
 | 
			
		||||
                if (ds == null) return loc1;
 | 
			
		||||
 | 
			
		||||
                var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>();
 | 
			
		||||
                foreach (object[] row in ds)
 | 
			
		||||
                {
 | 
			
		||||
                    var table_id = string.Concat(row[0]);
 | 
			
		||||
                    var column = row[1] as string[];
 | 
			
		||||
                    var fk_id = string.Concat(row[2]);
 | 
			
		||||
                    var ref_table_id = string.Concat(row[3]);
 | 
			
		||||
                    var is_foreign_key = string.Concat(row[4]) == "1";
 | 
			
		||||
                    var referenced_column = row[5] as string[];
 | 
			
		||||
                    var referenced_db = string.Concat(row[6]);
 | 
			
		||||
                    var referenced_table = string.Concat(row[7]);
 | 
			
		||||
 | 
			
		||||
                    if (loc2.ContainsKey(ref_table_id) == false) continue;
 | 
			
		||||
 | 
			
		||||
                    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 = loc2[ref_table_id] });
 | 
			
		||||
 | 
			
		||||
                    for (int a = 0; a < column.Length; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        loc13.Columns.Add(loc3[table_id][column[a]]);
 | 
			
		||||
                        loc13.ReferencedColumns.Add(loc3[ref_table_id][referenced_column[a]]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                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)
 | 
			
		||||
                        {
 | 
			
		||||
                            loc5.IsPrimary = true;
 | 
			
		||||
                            loc4.Primarys.Add(loc5);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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();
 | 
			
		||||
                tables.AddRange(loc1);
 | 
			
		||||
            }
 | 
			
		||||
            return tables;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
 | 
			
		||||
        {
 | 
			
		||||
            if (database == null || database.Length == 0) return new List<DbEnumInfo>();
 | 
			
		||||
            var drs = _orm.Ado.Query<(string name, string label)>(CommandType.Text, _commonUtils.FormatSql(@"
 | 
			
		||||
select
 | 
			
		||||
ns.nspname || '.' || a.typname,
 | 
			
		||||
b.enumlabel
 | 
			
		||||
from pg_type a
 | 
			
		||||
inner join pg_enum b on b.enumtypid = a.oid
 | 
			
		||||
inner join pg_namespace ns on ns.oid = a.typnamespace
 | 
			
		||||
where a.typtype = 'e' and ns.nspname in (SELECT ""schema_name"" FROM information_schema.schemata where catalog_name in {0})", database));
 | 
			
		||||
            var ret = new Dictionary<string, Dictionary<string, string>>();
 | 
			
		||||
            foreach (var dr in drs)
 | 
			
		||||
            {
 | 
			
		||||
                if (ret.TryGetValue(dr.name, out var labels) == false) ret.Add(dr.name, labels = new Dictionary<string, string>());
 | 
			
		||||
                var key = dr.label;
 | 
			
		||||
                if (Regex.IsMatch(key, @"^[\u0391-\uFFE5a-zA-Z_\$][\u0391-\uFFE5a-zA-Z_\$\d]*$") == false)
 | 
			
		||||
                    key = $"Unkown{ret[dr.name].Count + 1}";
 | 
			
		||||
                if (labels.ContainsKey(key) == false) labels.Add(key, dr.label);
 | 
			
		||||
            }
 | 
			
		||||
            return ret.Select(a => new DbEnumInfo { Name = a.Key, Labels = a.Value }).ToList();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,538 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
    class OdbcPostgreSQLExpression : CommonExpression
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public OdbcPostgreSQLExpression(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.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)})::varchar not in ('0','false','f','no'))";
 | 
			
		||||
                            case "System.Byte": return $"({getExp(operandExp)})::int2";
 | 
			
		||||
                            case "System.Char": return $"substr(({getExp(operandExp)})::char, 1, 1)";
 | 
			
		||||
                            case "System.DateTime": return $"({getExp(operandExp)})::timestamp";
 | 
			
		||||
                            case "System.Decimal": return $"({getExp(operandExp)})::numeric";
 | 
			
		||||
                            case "System.Double": return $"({getExp(operandExp)})::float8";
 | 
			
		||||
                            case "System.Int16": return $"({getExp(operandExp)})::int2";
 | 
			
		||||
                            case "System.Int32": return $"({getExp(operandExp)})::int4";
 | 
			
		||||
                            case "System.Int64": return $"({getExp(operandExp)})::int8";
 | 
			
		||||
                            case "System.SByte": return $"({getExp(operandExp)})::int2";
 | 
			
		||||
                            case "System.Single": return $"({getExp(operandExp)})::float4";
 | 
			
		||||
                            case "System.String": return $"({getExp(operandExp)})::varchar";
 | 
			
		||||
                            case "System.UInt16": return $"({getExp(operandExp)})::int2";
 | 
			
		||||
                            case "System.UInt32": return $"({getExp(operandExp)})::int4";
 | 
			
		||||
                            case "System.UInt64": return $"({getExp(operandExp)})::int8";
 | 
			
		||||
                            case "System.Guid": return $"({getExp(operandExp)})::uuid";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.ArrayLength:
 | 
			
		||||
                    var arrOperExp = getExp((exp as UnaryExpression).Operand);
 | 
			
		||||
                    if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)";
 | 
			
		||||
                    return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end";
 | 
			
		||||
                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])})::varchar not in ('0','false','f','no'))";
 | 
			
		||||
                                case "System.Byte": return $"({getExp(callExp.Arguments[0])})::int2";
 | 
			
		||||
                                case "System.Char": return $"substr(({getExp(callExp.Arguments[0])})::char, 1, 1)";
 | 
			
		||||
                                case "System.DateTime": return $"({getExp(callExp.Arguments[0])})::timestamp";
 | 
			
		||||
                                case "System.Decimal": return $"({getExp(callExp.Arguments[0])})::numeric";
 | 
			
		||||
                                case "System.Double": return $"({getExp(callExp.Arguments[0])})::float8";
 | 
			
		||||
                                case "System.Int16": return $"({getExp(callExp.Arguments[0])})::int2";
 | 
			
		||||
                                case "System.Int32": return $"({getExp(callExp.Arguments[0])})::int4";
 | 
			
		||||
                                case "System.Int64": return $"({getExp(callExp.Arguments[0])})::int8";
 | 
			
		||||
                                case "System.SByte": return $"({getExp(callExp.Arguments[0])})::int2";
 | 
			
		||||
                                case "System.Single": return $"({getExp(callExp.Arguments[0])})::float4";
 | 
			
		||||
                                case "System.UInt16": return $"({getExp(callExp.Arguments[0])})::int2";
 | 
			
		||||
                                case "System.UInt32": return $"({getExp(callExp.Arguments[0])})::int4";
 | 
			
		||||
                                case "System.UInt64": return $"({getExp(callExp.Arguments[0])})::int8";
 | 
			
		||||
                                case "System.Guid": return $"({getExp(callExp.Arguments[0])})::uuid";
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "NewGuid":
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "Next":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "(random()*1000000000)::int4";
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "NextDouble":
 | 
			
		||||
                            if (callExp.Object?.Type == typeof(Random)) return "random()";
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "Random":
 | 
			
		||||
                            if (callExp.Method.DeclaringType.IsNumberType()) return "random()";
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "ToString":
 | 
			
		||||
                            if (callExp.Object != null) return callExp.Arguments.Count == 0 ? $"({getExp(callExp.Object)})::varchar" : null;
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    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 == null) objType = callExp.Method.DeclaringType;
 | 
			
		||||
                    if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType))
 | 
			
		||||
                    {
 | 
			
		||||
                        var left = objExp == null ? null : getExp(objExp);
 | 
			
		||||
                        if (objType.FullName == typeof(Dictionary<string, string>).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            switch (callExp.Method.Name)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "Contains":
 | 
			
		||||
                                    var right = getExp(callExp.Arguments[argIndex]);
 | 
			
		||||
                                    return $"({left} @> ({right}))";
 | 
			
		||||
                                case "ContainsKey": return $"({left} ? {getExp(callExp.Arguments[argIndex])})";
 | 
			
		||||
                                case "Concat": return $"({left} || {getExp(callExp.Arguments[argIndex])})";
 | 
			
		||||
                                case "GetLength":
 | 
			
		||||
                                case "GetLongLength":
 | 
			
		||||
                                case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end";
 | 
			
		||||
                                case "Keys": return $"akeys({left})";
 | 
			
		||||
                                case "Values": return $"avals({left})";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        switch (callExp.Method.Name)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "Any":
 | 
			
		||||
                                if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
 | 
			
		||||
                                return $"(case when {left} is null then 0 else array_length({left},1) end > 0)";
 | 
			
		||||
                            case "Contains":
 | 
			
		||||
                                //判断 in 或 array @> array
 | 
			
		||||
                                var right1 = getExp(callExp.Arguments[argIndex]);
 | 
			
		||||
                                if (left.StartsWith("array[") || left.EndsWith("]"))
 | 
			
		||||
                                    return $"{right1} in ({left.Substring(6, left.Length - 7)})";
 | 
			
		||||
                                if (left.StartsWith("(") || left.EndsWith(")"))
 | 
			
		||||
                                    return $"{right1} in {left}";
 | 
			
		||||
                                if (right1.StartsWith("(") || right1.EndsWith(")")) right1 = $"array[{right1.TrimStart('(').TrimEnd(')')}]";
 | 
			
		||||
                                return $"({left} @> array[{right1}])";
 | 
			
		||||
                            case "Concat":
 | 
			
		||||
                                if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
 | 
			
		||||
                                var right2 = getExp(callExp.Arguments[argIndex]);
 | 
			
		||||
                                if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]";
 | 
			
		||||
                                return $"({left} || {right2})";
 | 
			
		||||
                            case "GetLength":
 | 
			
		||||
                            case "GetLongLength":
 | 
			
		||||
                            case "Length":
 | 
			
		||||
                            case "Count":
 | 
			
		||||
                                if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
 | 
			
		||||
                                return $"case when {left} is null then 0 else array_length({left},1) end";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.MemberAccess:
 | 
			
		||||
                    var memExp = exp as MemberExpression;
 | 
			
		||||
                    var memParentExp = memExp.Expression?.Type;
 | 
			
		||||
                    if (memParentExp?.FullName == "System.Byte[]") return null;
 | 
			
		||||
                    if (memParentExp != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (memParentExp.IsArray == true)
 | 
			
		||||
                        {
 | 
			
		||||
                            var left = getExp(memExp.Expression);
 | 
			
		||||
                            if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
 | 
			
		||||
                            switch (memExp.Member.Name)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "Length":
 | 
			
		||||
                                case "Count": return $"case when {left} is null then 0 else array_length({left},1) end";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        switch (memParentExp.FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "Newtonsoft.Json.Linq.JToken":
 | 
			
		||||
                            case "Newtonsoft.Json.Linq.JObject":
 | 
			
		||||
                            case "Newtonsoft.Json.Linq.JArray":
 | 
			
		||||
                                var left = getExp(memExp.Expression);
 | 
			
		||||
                                switch (memExp.Member.Name)
 | 
			
		||||
                                {
 | 
			
		||||
                                    case "Count": return $"jsonb_array_length(coalesce({left},'[]'))";
 | 
			
		||||
                                }
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (memParentExp.FullName == typeof(Dictionary<string, string>).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            var left = getExp(memExp.Expression);
 | 
			
		||||
                            switch (memExp.Member.Name)
 | 
			
		||||
                            {
 | 
			
		||||
                                case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end";
 | 
			
		||||
                                case "Keys": return $"akeys({left})";
 | 
			
		||||
                                case "Values": return $"avals({left})";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case ExpressionType.NewArrayInit:
 | 
			
		||||
                    var arrExp = exp as NewArrayExpression;
 | 
			
		||||
                    var arrSb = new StringBuilder();
 | 
			
		||||
                    arrSb.Append("array[");
 | 
			
		||||
                    for (var a = 0; a < arrExp.Expressions.Count; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (a > 0) arrSb.Append(",");
 | 
			
		||||
                        arrSb.Append(getExp(arrExp.Expressions[a]));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (arrSb.Length == 1) arrSb.Append("NULL");
 | 
			
		||||
                    return arrSb.Append("]").ToString();
 | 
			
		||||
                case ExpressionType.ListInit:
 | 
			
		||||
                    var listExp = exp as ListInitExpression;
 | 
			
		||||
                    var listSb = new StringBuilder();
 | 
			
		||||
                    listSb.Append("(");
 | 
			
		||||
                    for (var a = 0; a < listExp.Initializers.Count; a++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (listExp.Initializers[a].Arguments.Any() == false) continue;
 | 
			
		||||
                        if (a > 0) listSb.Append(",");
 | 
			
		||||
                        listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (listSb.Length == 1) listSb.Append("NULL");
 | 
			
		||||
                    return listSb.Append(")").ToString();
 | 
			
		||||
                case ExpressionType.New:
 | 
			
		||||
                    var newExp = exp as NewExpression;
 | 
			
		||||
                    if (typeof(IList).IsAssignableFrom(newExp.Type))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (newExp.Arguments.Count == 0) return "(NULL)";
 | 
			
		||||
                        if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
 | 
			
		||||
                        return getExp(newExp.Arguments[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                    return null;
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Empty": return "''";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Length": return $"char_length({left})";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Now": return "current_timestamp";
 | 
			
		||||
                    case "UtcNow": return "(current_timestamp at time zone 'UTC')";
 | 
			
		||||
                    case "Today": return "current_date";
 | 
			
		||||
                    case "MinValue": return "'0001/1/1 0:00:00'::timestamp";
 | 
			
		||||
                    case "MaxValue": return "'9999/12/31 23:59:59'::timestamp";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Date": return $"({left})::date";
 | 
			
		||||
                case "TimeOfDay": return $"(extract(epoch from ({left})::time)*1000000)";
 | 
			
		||||
                case "DayOfWeek": return $"extract(dow from ({left})::timestamp)";
 | 
			
		||||
                case "Day": return $"extract(day from ({left})::timestamp)";
 | 
			
		||||
                case "DayOfYear": return $"extract(doy from ({left})::timestamp)";
 | 
			
		||||
                case "Month": return $"extract(month from ({left})::timestamp)";
 | 
			
		||||
                case "Year": return $"extract(year from ({left})::timestamp)";
 | 
			
		||||
                case "Hour": return $"extract(hour from ({left})::timestamp)";
 | 
			
		||||
                case "Minute": return $"extract(minute from ({left})::timestamp)";
 | 
			
		||||
                case "Second": return $"extract(second from ({left})::timestamp)";
 | 
			
		||||
                case "Millisecond": return $"(extract(milliseconds from ({left})::timestamp)-extract(second from ({left})::timestamp)*1000)";
 | 
			
		||||
                case "Ticks": return $"(extract(epoch from ({left})::timestamp)*10000000+621355968000000000)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Expression == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Member.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Zero": return "0";
 | 
			
		||||
                    case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
 | 
			
		||||
                    case "MaxValue": return "922337203685477580";
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            var left = ExpressionLambdaToSql(exp.Expression, tsc);
 | 
			
		||||
            switch (exp.Member.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Days": return $"floor(({left})/{(long)1000000 * 60 * 60 * 24})";
 | 
			
		||||
                case "Hours": return $"floor(({left})/{(long)1000000 * 60 * 60}%24)";
 | 
			
		||||
                case "Milliseconds": return $"(floor(({left})/1000)::int8%1000)";
 | 
			
		||||
                case "Minutes": return $"(floor(({left})/{(long)1000000 * 60})::int8%60)";
 | 
			
		||||
                case "Seconds": return $"(floor(({left})/1000000)::int8%60)";
 | 
			
		||||
                case "Ticks": return $"(({left})*10)";
 | 
			
		||||
                case "TotalDays": return $"(({left})/{(long)1000000 * 60 * 60 * 24})";
 | 
			
		||||
                case "TotalHours": return $"(({left})/{(long)1000000 * 60 * 60})";
 | 
			
		||||
                case "TotalMilliseconds": return $"(({left})/1000)";
 | 
			
		||||
                case "TotalMinutes": return $"(({left})/{(long)1000000 * 60})";
 | 
			
		||||
                case "TotalSeconds": return $"(({left})/1000000)";
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "IsNullOrEmpty":
 | 
			
		||||
                        var arg1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"({arg1} is null or {arg1} = '')";
 | 
			
		||||
                    case "IsNullOrWhiteSpace":
 | 
			
		||||
                        var arg2 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')";
 | 
			
		||||
                    case "Concat":
 | 
			
		||||
                        return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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";
 | 
			
		||||
                        var likeOpt = "LIKE";
 | 
			
		||||
                        if (exp.Arguments.Count > 1)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (exp.Arguments[1].Type == typeof(bool) ||
 | 
			
		||||
                                exp.Arguments[1].Type == typeof(StringComparison) && getExp(exp.Arguments[0]).Contains("IgnoreCase")) likeOpt = "ILIKE";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (exp.Method.Name == "StartsWith") return $"({left}) {likeOpt} {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(({args0Value})::varchar || '%')")}";
 | 
			
		||||
                        if (exp.Method.Name == "EndsWith") return $"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::varchar)")}";
 | 
			
		||||
                        if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) {likeOpt} {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
 | 
			
		||||
                        return $"({left}) {likeOpt} ('%' || ({args0Value})::varchar || '%')";
 | 
			
		||||
                    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": return $"(strpos({left}, {getExp(exp.Arguments[0])})-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})";
 | 
			
		||||
                        }
 | 
			
		||||
                        var trimArg1 = "";
 | 
			
		||||
                        var trimArg2 = "";
 | 
			
		||||
                        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)
 | 
			
		||||
                            {
 | 
			
		||||
                                var trimChr = getExp(argsTrim01).Trim('\'');
 | 
			
		||||
                                if (trimChr.Length == 1) trimArg1 += trimChr;
 | 
			
		||||
                                else trimArg2 += $" || ({trimChr})";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
 | 
			
		||||
                        if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
 | 
			
		||||
                        if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
 | 
			
		||||
                        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])})::varchar)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            switch (exp.Method.Name)
 | 
			
		||||
            {
 | 
			
		||||
                case "Abs": return $"abs({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Sign": return $"sign({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Floor": return $"floor({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Round":
 | 
			
		||||
                    if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    return $"round({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Exp": return $"exp({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log": return $"log({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Log10": return $"log10({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Cos": return $"cos({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Sin": return $"sin({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Tan": return $"tan({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Acos": return $"acos({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Asin": return $"asin({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Atan": return $"atan({getExp(exp.Arguments[0])})";
 | 
			
		||||
                case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
 | 
			
		||||
                case "Truncate": return $"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(epoch from ({getExp(exp.Arguments[0])})::timestamp-({getExp(exp.Arguments[1])})::timestamp)";
 | 
			
		||||
                    case "DaysInMonth": return $"extract(day from ({getExp(exp.Arguments[0])} || '-' || {getExp(exp.Arguments[1])} || '-01')::timestamp+'1 month'::interval-'1 day'::interval)";
 | 
			
		||||
                    case "Equals": return $"(({getExp(exp.Arguments[0])})::timestamp = ({getExp(exp.Arguments[1])})::timestamp)";
 | 
			
		||||
 | 
			
		||||
                    case "IsLeapYear":
 | 
			
		||||
                        var isLeapYearArgs1 = getExp(exp.Arguments[0]);
 | 
			
		||||
                        return $"(({isLeapYearArgs1})::int8%4=0 AND ({isLeapYearArgs1})::int8%100<>0 OR ({isLeapYearArgs1})::int8%400=0)";
 | 
			
		||||
 | 
			
		||||
                    case "Parse": return $"({getExp(exp.Arguments[0])})::timestamp";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return $"({getExp(exp.Arguments[0])})::timestamp";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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})::timestamp+(({args1})||' microseconds')::interval)";
 | 
			
		||||
                    case "AddDays": return $"(({left})::timestamp+(({args1})||' day')::interval)";
 | 
			
		||||
                    case "AddHours": return $"(({left})::timestamp+(({args1})||' hour')::interval)";
 | 
			
		||||
                    case "AddMilliseconds": return $"(({left})::timestamp+(({args1})||' milliseconds')::interval)";
 | 
			
		||||
                    case "AddMinutes": return $"(({left})::timestamp+(({args1})||' minute')::interval)";
 | 
			
		||||
                    case "AddMonths": return $"(({left})::timestamp+(({args1})||' month')::interval)";
 | 
			
		||||
                    case "AddSeconds": return $"(({left})::timestamp+(({args1})||' second')::interval)";
 | 
			
		||||
                    case "AddTicks": return $"(({left})::timestamp+(({args1})/10||' microseconds')::interval)";
 | 
			
		||||
                    case "AddYears": return $"(({left})::timestamp+(({args1})||' year')::interval)";
 | 
			
		||||
                    case "Subtract":
 | 
			
		||||
                        switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.DateTime": return $"(extract(epoch from ({left})::timestamp-({args1})::timestamp)*1000000)";
 | 
			
		||||
                            case "System.TimeSpan": return $"(({left})::timestamp-(({args1})||' microseconds')::interval)";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = ({args1})::timestamp)";
 | 
			
		||||
                    case "CompareTo": return $"extract(epoch from ({left})::timestamp-({args1})::timestamp)";
 | 
			
		||||
                    case "ToString": return exp.Arguments.Count == 0 ? $"to_char({left}, 'YYYY-MM-DD HH24:MI:SS.US')" : null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc)
 | 
			
		||||
        {
 | 
			
		||||
            Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
 | 
			
		||||
            if (exp.Object == null)
 | 
			
		||||
            {
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
 | 
			
		||||
                    case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
 | 
			
		||||
                    case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})";
 | 
			
		||||
                    case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})";
 | 
			
		||||
                    case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)";
 | 
			
		||||
                    case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})";
 | 
			
		||||
                    case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)";
 | 
			
		||||
                    case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)";
 | 
			
		||||
                    case "Parse": return $"({getExp(exp.Arguments[0])})::int8";
 | 
			
		||||
                    case "ParseExact":
 | 
			
		||||
                    case "TryParse":
 | 
			
		||||
                    case "TryParseExact": return $"({getExp(exp.Arguments[0])})::int8";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var left = getExp(exp.Object);
 | 
			
		||||
                var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
 | 
			
		||||
                switch (exp.Method.Name)
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({args1}))";
 | 
			
		||||
                    case "ToString": return $"({left})::varchar";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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])})::varchar not in ('0','false','f','no'))";
 | 
			
		||||
                    case "ToByte": return $"({getExp(exp.Arguments[0])})::int2";
 | 
			
		||||
                    case "ToChar": return $"substr(({getExp(exp.Arguments[0])})::char, 1, 1)";
 | 
			
		||||
                    case "ToDateTime": return $"({getExp(exp.Arguments[0])})::timestamp";
 | 
			
		||||
                    case "ToDecimal": return $"({getExp(exp.Arguments[0])})::numeric";
 | 
			
		||||
                    case "ToDouble": return $"({getExp(exp.Arguments[0])})::float8";
 | 
			
		||||
                    case "ToInt16": return $"({getExp(exp.Arguments[0])})::int2";
 | 
			
		||||
                    case "ToInt32": return $"({getExp(exp.Arguments[0])})::int4";
 | 
			
		||||
                    case "ToInt64": return $"({getExp(exp.Arguments[0])})::int8";
 | 
			
		||||
                    case "ToSByte": return $"({getExp(exp.Arguments[0])})::int2";
 | 
			
		||||
                    case "ToSingle": return $"({getExp(exp.Arguments[0])})::float4";
 | 
			
		||||
                    case "ToString": return $"({getExp(exp.Arguments[0])})::varchar";
 | 
			
		||||
                    case "ToUInt16": return $"({getExp(exp.Arguments[0])})::int2";
 | 
			
		||||
                    case "ToUInt32": return $"({getExp(exp.Arguments[0])})::int4";
 | 
			
		||||
                    case "ToUInt64": return $"({getExp(exp.Arguments[0])})::int8";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.CommonProvider;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class OdbcPostgreSQLProvider<TMark> : IFreeSql<TMark>
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        static OdbcPostgreSQLProvider()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ISelect<T1> Select<T1>() where T1 : class => new OdbcPostgreSQLSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
 | 
			
		||||
        public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new OdbcPostgreSQLSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public IInsert<T1> Insert<T1>() where T1 : class => new OdbcPostgreSQLInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        public IInsert<T1> Insert<T1>(T1 source) where T1 : class => this.Insert<T1>().AppendData(source);
 | 
			
		||||
        public IInsert<T1> Insert<T1>(T1[] source) where T1 : class => this.Insert<T1>().AppendData(source);
 | 
			
		||||
        public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => this.Insert<T1>().AppendData(source);
 | 
			
		||||
        public IUpdate<T1> Update<T1>() where T1 : class => new OdbcPostgreSQLUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
 | 
			
		||||
        public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new OdbcPostgreSQLUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
        public IDelete<T1> Delete<T1>() where T1 : class => new OdbcPostgreSQLDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
 | 
			
		||||
        public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new OdbcPostgreSQLDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
 | 
			
		||||
 | 
			
		||||
        public IAdo Ado { get; }
 | 
			
		||||
        public IAop Aop { get; }
 | 
			
		||||
        public ICodeFirst CodeFirst { get; }
 | 
			
		||||
        public IDbFirst DbFirst { get; }
 | 
			
		||||
        public OdbcPostgreSQLProvider(string masterConnectionString, string[] slaveConnectionString)
 | 
			
		||||
        {
 | 
			
		||||
            this.InternalCommonUtils = new OdbcPostgreSQLUtils(this);
 | 
			
		||||
            this.InternalCommonExpression = new OdbcPostgreSQLExpression(this.InternalCommonUtils);
 | 
			
		||||
 | 
			
		||||
            this.Ado = new OdbcPostgreSQLAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
 | 
			
		||||
            this.Aop = new AopProvider();
 | 
			
		||||
 | 
			
		||||
            this.DbFirst = new OdbcPostgreSQLDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
            this.CodeFirst = new OdbcPostgreSQLCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal CommonUtils InternalCommonUtils { get; }
 | 
			
		||||
        internal CommonExpression InternalCommonExpression { get; }
 | 
			
		||||
 | 
			
		||||
        public void Transaction(Action handler) => Ado.Transaction(handler);
 | 
			
		||||
 | 
			
		||||
        public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
 | 
			
		||||
 | 
			
		||||
        ~OdbcPostgreSQLProvider()
 | 
			
		||||
        {
 | 
			
		||||
            this.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
        bool _isdisposed = false;
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            if (_isdisposed) return;
 | 
			
		||||
            (this.Ado as AdoProvider)?.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,160 @@
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.PostgreSQL
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class OdbcPostgreSQLUtils : CommonUtils
 | 
			
		||||
    {
 | 
			
		||||
        public OdbcPostgreSQLUtils(IFreeSql orm) : base(orm)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static Array getParamterArrayValue(Type arrayType, object value, object defaultValue)
 | 
			
		||||
        {
 | 
			
		||||
            var valueArr = value as Array;
 | 
			
		||||
            var len = valueArr.GetLength(0);
 | 
			
		||||
            var ret = Array.CreateInstance(arrayType, len);
 | 
			
		||||
            for (var a = 0; a < len; a++)
 | 
			
		||||
            {
 | 
			
		||||
                var item = valueArr.GetValue(a);
 | 
			
		||||
                ret.SetValue(item == null ? defaultValue : getParamterValue(item.GetType(), item, 1), a);
 | 
			
		||||
            }
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        static Dictionary<string, Func<object, object>> dicGetParamterValue = new Dictionary<string, Func<object, object>> {
 | 
			
		||||
            { typeof(uint).FullName, a => long.Parse(string.Concat(a)) }, { typeof(uint[]).FullName, a => getParamterArrayValue(typeof(long), a, 0) }, { typeof(uint?[]).FullName, a => getParamterArrayValue(typeof(long?), a, null) },
 | 
			
		||||
            { typeof(ulong).FullName, a => decimal.Parse(string.Concat(a)) }, { typeof(ulong[]).FullName, a => getParamterArrayValue(typeof(decimal), a, 0) }, { typeof(ulong?[]).FullName, a => getParamterArrayValue(typeof(decimal?), a, null) },
 | 
			
		||||
            { typeof(ushort).FullName, a => int.Parse(string.Concat(a)) }, { typeof(ushort[]).FullName, a => getParamterArrayValue(typeof(int), a, 0) }, { typeof(ushort?[]).FullName, a => getParamterArrayValue(typeof(int?), a, null) },
 | 
			
		||||
            { typeof(byte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(byte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(byte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) },
 | 
			
		||||
            { typeof(sbyte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(sbyte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(sbyte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) },
 | 
			
		||||
        };
 | 
			
		||||
        static object getParamterValue(Type type, object value, int level = 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (type.FullName == "System.Byte[]") return value;
 | 
			
		||||
            if (type.IsArray && level == 0)
 | 
			
		||||
            {
 | 
			
		||||
                var elementType = type.GetElementType();
 | 
			
		||||
                Type enumType = null;
 | 
			
		||||
                if (elementType.IsEnum) enumType = elementType;
 | 
			
		||||
                else if (elementType.IsNullableType() && elementType.GenericTypeArguments.First().IsEnum) enumType = elementType.GenericTypeArguments.First();
 | 
			
		||||
                if (enumType != null) return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
 | 
			
		||||
                    getParamterArrayValue(typeof(long), value, elementType.IsEnum ? null : Enum.GetValues(enumType).GetValue(0)) :
 | 
			
		||||
                    getParamterArrayValue(typeof(int), value, elementType.IsEnum ? null : Enum.GetValues(enumType).GetValue(0));
 | 
			
		||||
                return dicGetParamterValue.TryGetValue(type.FullName, out var trydicarr) ? trydicarr(value) : value;
 | 
			
		||||
            }
 | 
			
		||||
            if (type.IsNullableType()) type = type.GenericTypeArguments.First();
 | 
			
		||||
            if (type.IsEnum) return (int)value;
 | 
			
		||||
            if (dicGetParamterValue.TryGetValue(type.FullName, out var trydic)) return trydic(value);
 | 
			
		||||
            return value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
 | 
			
		||||
            if (value != null) value = getParamterValue(type, value);
 | 
			
		||||
            var ret = new OdbcParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
 | 
			
		||||
            //if (value.GetType().IsEnum || value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true) {
 | 
			
		||||
            //	ret.DataTypeName = "";
 | 
			
		||||
            //} else {
 | 
			
		||||
            var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
            if (tp != null) ret.OdbcType = (OdbcType)tp.Value;
 | 
			
		||||
            //}
 | 
			
		||||
            _params?.Add(ret);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
 | 
			
		||||
            Utils.GetDbParamtersByObject<OdbcParameter>(sql, obj, null, (name, type, value) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (value != null) value = getParamterValue(type, value);
 | 
			
		||||
                var ret = new OdbcParameter { ParameterName = $"@{name}", Value = value };
 | 
			
		||||
                //if (value.GetType().IsEnum || value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true) {
 | 
			
		||||
                //	ret.DataTypeName = "";
 | 
			
		||||
                //} else {
 | 
			
		||||
                var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
 | 
			
		||||
                if (tp != null) ret.OdbcType = (OdbcType)tp.Value;
 | 
			
		||||
                //}
 | 
			
		||||
                return ret;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcPostgreSQL(args);
 | 
			
		||||
        public override string QuoteSqlName(string name)
 | 
			
		||||
        {
 | 
			
		||||
            var nametrim = name.Trim();
 | 
			
		||||
            if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
 | 
			
		||||
                return nametrim; //原生SQL
 | 
			
		||||
            return $"\"{nametrim.Trim('"').Replace(".", "\".\"")}\"";
 | 
			
		||||
        }
 | 
			
		||||
        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 QuoteParamterName(string name) => $"@{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
 | 
			
		||||
        public override string IsNull(string sql, object value) => $"coalesce({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) => $"{left} % {right}";
 | 
			
		||||
        public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}";
 | 
			
		||||
 | 
			
		||||
        public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
 | 
			
		||||
        public override string QuoteReadColumn(Type type, string columnName) => columnName;
 | 
			
		||||
 | 
			
		||||
        public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (value == null) return "NULL";
 | 
			
		||||
            value = getParamterValue(type, value);
 | 
			
		||||
            var type2 = value.GetType();
 | 
			
		||||
            if (type2 == typeof(byte[]))
 | 
			
		||||
            {
 | 
			
		||||
                var bytes = value as byte[];
 | 
			
		||||
                var sb = new StringBuilder().Append("'\\x");
 | 
			
		||||
                foreach (var vc in bytes)
 | 
			
		||||
                {
 | 
			
		||||
                    if (vc < 10) sb.Append("0");
 | 
			
		||||
                    sb.Append(vc.ToString("X"));
 | 
			
		||||
                }
 | 
			
		||||
                return sb.Append("'").ToString(); //val = Encoding.UTF8.GetString(val as byte[]);
 | 
			
		||||
            }
 | 
			
		||||
            else if (type2 == typeof(TimeSpan) || type2 == typeof(TimeSpan?))
 | 
			
		||||
            {
 | 
			
		||||
                var ts = (TimeSpan)value;
 | 
			
		||||
                return $"'{Math.Min(24, (int)Math.Floor(ts.TotalHours))}:{ts.Minutes}:{ts.Seconds}'";
 | 
			
		||||
            }
 | 
			
		||||
            else if (value is Array)
 | 
			
		||||
            {
 | 
			
		||||
                var valueArr = value as Array;
 | 
			
		||||
                var eleType = type2.GetElementType();
 | 
			
		||||
                var len = valueArr.GetLength(0);
 | 
			
		||||
                var sb = new StringBuilder().Append("ARRAY[");
 | 
			
		||||
                for (var a = 0; a < len; a++)
 | 
			
		||||
                {
 | 
			
		||||
                    var item = valueArr.GetValue(a);
 | 
			
		||||
                    if (a > 0) sb.Append(",");
 | 
			
		||||
                    sb.Append(GetNoneParamaterSqlValue(specialParams, eleType, item));
 | 
			
		||||
                }
 | 
			
		||||
                sb.Append("]");
 | 
			
		||||
                var dbinfo = _orm.CodeFirst.GetDbInfo(type);
 | 
			
		||||
                if (dbinfo.HasValue) sb.Append("::").Append(dbinfo.Value.dbtype);
 | 
			
		||||
                return sb.ToString();
 | 
			
		||||
            }
 | 
			
		||||
            else if (dicGetParamterValue.ContainsKey(type2.FullName))
 | 
			
		||||
            {
 | 
			
		||||
                value = string.Concat(value);
 | 
			
		||||
            }
 | 
			
		||||
            return FormatSql("{0}", value, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
using FreeSql.DataAnnotations;
 | 
			
		||||
using FreeSql.DatabaseModel;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal;
 | 
			
		||||
using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Odbc;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Odbc.SqlServer
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -362,11 +362,11 @@ namespace FreeSql.Odbc.SqlServer
 | 
			
		||||
                        switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName)
 | 
			
		||||
                        {
 | 
			
		||||
                            case "System.DateTime": return $"datediff(second, {args1}, {left})";
 | 
			
		||||
                            case "System.TimeSpan": return $"dateadd(second, {args1}*-1, {left})";
 | 
			
		||||
                            case "System.TimeSpan": return $"dateadd(second, ({args1})*-1, {left})";
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "CompareTo": return $"datediff(second,{getExp(exp.Arguments[0])},{left})";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"datediff(second,{args1},{left})";
 | 
			
		||||
                    case "ToString": return exp.Arguments.Count == 0 ? $"convert(varchar, {left}, 121)" : null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -401,8 +401,8 @@ namespace FreeSql.Odbc.SqlServer
 | 
			
		||||
                {
 | 
			
		||||
                    case "Add": return $"({left}+{args1})";
 | 
			
		||||
                    case "Subtract": return $"({left}-({args1}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
 | 
			
		||||
                    case "Equals": return $"({left} = {args1})";
 | 
			
		||||
                    case "CompareTo": return $"({left}-({args1}))";
 | 
			
		||||
                    case "ToString": return $"cast({left} as varchar)";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								Providers/FreeSql.Provider.Odbc/readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Providers/FreeSql.Provider.Odbc/readme.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
FreeSql.Provider.Odbc 实现 ODBC 访问数据库,ODBC 属于比较原始的技术,更新慢,各大数据库厂支持得标准不一,不到万不得已最好别用 odbc,坑比较多。
 | 
			
		||||
 | 
			
		||||
FreeSql.Provider.Odbc 做了四种数据库的专用实现:SqlServer、PostgreSQL、Oracle、MySql,和一种通用实现。
 | 
			
		||||
 | 
			
		||||
专用实现比较有针对性,和原来的 FreeSql.Provider.SqlServer ado.net 相比,只支持较少的基础类型,其他功能几乎都有,包括 CodeFirst 自动迁移。
 | 
			
		||||
 | 
			
		||||
# 通用实现
 | 
			
		||||
 | 
			
		||||
通用实现为了让用户自己适配更多的数据库,比如连接 mssql 2000、db2 等数据库。
 | 
			
		||||
 | 
			
		||||
使用者只需求重写类 FreeSql.Odbc.Default.OdbcAdapter 就可以自定义访问不同的数据库。
 | 
			
		||||
 | 
			
		||||
我们默认做了一套 sqlserver 的语法和映射适配,代码在 Default/OdbcAdapter.cs,请查看代码了解。
 | 
			
		||||
 | 
			
		||||
```csharp
 | 
			
		||||
class Mssql2000Adapter : FreeSql.Odbc.Default.OdbcAdapter
 | 
			
		||||
{
 | 
			
		||||
    public override string InsertAfterGetIdentitySql => "SELECT SCOPE_IDENTITY()";
 | 
			
		||||
    //可以重写更多的设置
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fsql.SetOdbcAdapter(new Mssql2000Adapter());
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
适配好新的 OdbcAdapter 后,请在 FreeSqlBuilder.Build 之后调用 IFreeSql.SetOdbcAdapter 方法生效。
 | 
			
		||||
		Reference in New Issue
	
	Block a user