Add QuestDb

This commit is contained in:
dailyccc 2023-02-21 09:36:41 +08:00
parent fbd062ab2d
commit 0ef87ad028
20 changed files with 4894 additions and 0 deletions

View File

@ -0,0 +1,217 @@
using FreeSql.Aop;
using FreeSql.Internal.Model;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql.QuestDb.Curd
{
public class OnConflictDoUpdate<T1> where T1 : class
{
internal QuestDbInsert<T1> _pgsqlInsert;
internal QuestDbUpdate<T1> _pgsqlUpdatePriv;
internal QuestDbUpdate<T1> _pgsqlUpdate => _pgsqlUpdatePriv ??
(_pgsqlUpdatePriv = new QuestDbUpdate<T1>(_pgsqlInsert.InternalOrm, _pgsqlInsert.InternalCommonUtils, _pgsqlInsert.InternalCommonExpression, null) { InternalTableAlias = "EXCLUDED" }
.NoneParameter().SetSource(_pgsqlInsert.InternalSource) as QuestDbUpdate<T1>);
internal ColumnInfo[] _tempPrimarys;
bool _doNothing;
public OnConflictDoUpdate(IInsert<T1> insert, Expression<Func<T1, object>> columns = null)
{
_pgsqlInsert = insert as QuestDbInsert<T1>;
if (_pgsqlInsert == null) throw new Exception(CoreStrings.S_Features_Unique("OnConflictDoUpdate", "PostgreSQL"));
if (_pgsqlInsert._noneParameterFlag == "c") _pgsqlInsert._noneParameterFlag = "cu";
if (columns != null)
{
var colsList = new List<ColumnInfo>();
var cols = _pgsqlInsert.InternalCommonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, null, columns?.Body, false, null).ToDictionary(a => a, a => true);
foreach (var col in _pgsqlInsert.InternalTable.Columns.Values)
if (cols.ContainsKey(col.Attribute.Name))
colsList.Add(col);
_tempPrimarys = colsList.ToArray();
}
if (_tempPrimarys == null || _tempPrimarys.Any() == false)
_tempPrimarys = _pgsqlInsert.InternalTable.Primarys;
if (_tempPrimarys.Any() == false) throw new Exception(CoreStrings.S_OnConflictDoUpdate_MustIsPrimary);
}
protected void ClearData()
{
_pgsqlInsert.InternalClearData();
_pgsqlUpdatePriv = null;
}
public OnConflictDoUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns)
{
_pgsqlUpdate.IgnoreColumns(columns);
return this;
}
public OnConflictDoUpdate<T1> UpdateColumns(Expression<Func<T1, object>> columns)
{
_pgsqlUpdate.UpdateColumns(columns);
return this;
}
public OnConflictDoUpdate<T1> IgnoreColumns(string[] columns)
{
_pgsqlUpdate.IgnoreColumns(columns);
return this;
}
public OnConflictDoUpdate<T1> UpdateColumns(string[] columns)
{
_pgsqlUpdate.UpdateColumns(columns);
return this;
}
public OnConflictDoUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> column, TMember value)
{
_pgsqlUpdate.Set(column, value);
return this;
}
//由于表达式解析问题ON CONFLICT("id") DO UPDATE SET 需要指定表别名,如 Set(a => a.Clicks + 1) 解析会失败
//暂时不开放这个功能,如有需要使用 SetRaw("click = t.click + 1") 替代该操作
//public OnConflictDoUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> exp)
//{
// _pgsqlUpdate.Set(exp);
// return this;
//}
public OnConflictDoUpdate<T1> SetRaw(string sql)
{
_pgsqlUpdate.SetRaw(sql);
return this;
}
public OnConflictDoUpdate<T1> DoNothing()
{
_doNothing = true;
return this;
}
public string ToSql()
{
var sb = new StringBuilder();
sb.Append(_pgsqlInsert.ToSql()).Append("\r\nON CONFLICT(");
for (var a = 0; a < _tempPrimarys.Length; a++)
{
if (a > 0) sb.Append(", ");
sb.Append(_pgsqlInsert.InternalCommonUtils.QuoteSqlName(_tempPrimarys[a].Attribute.Name));
}
if (_doNothing)
{
sb.Append(") DO NOTHING");
}
else
{
sb.Append(") DO UPDATE SET\r\n");
if (_pgsqlUpdate._tempPrimarys.Any() == false) _pgsqlUpdate._tempPrimarys = _tempPrimarys;
var sbSetEmpty = _pgsqlUpdate.InternalSbSet.Length == 0;
var sbSetIncrEmpty = _pgsqlUpdate.InternalSbSetIncr.Length == 0;
if (sbSetEmpty == false || sbSetIncrEmpty == false)
{
if (sbSetEmpty == false) sb.Append(_pgsqlUpdate.InternalSbSet.ToString().Substring(2));
if (sbSetIncrEmpty == false) sb.Append(sbSetEmpty ? _pgsqlUpdate.InternalSbSetIncr.ToString().Substring(2) : _pgsqlUpdate.InternalSbSetIncr.ToString());
}
else
{
var colidx = 0;
foreach (var col in _pgsqlInsert.InternalTable.Columns.Values)
{
if (col.Attribute.IsPrimary || _pgsqlUpdate.InternalIgnore.ContainsKey(col.Attribute.Name)) continue;
if (colidx > 0) sb.Append(", \r\n");
if (col.Attribute.IsVersion == true && col.Attribute.MapType != typeof(byte[]))
{
var field = _pgsqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name);
sb.Append(field).Append(" = ").Append(_pgsqlInsert.InternalCommonUtils.QuoteSqlName(_pgsqlInsert.InternalTable.DbName)).Append(".").Append(field).Append(" + 1");
}
else if (_pgsqlInsert.InternalIgnore.ContainsKey(col.Attribute.Name))
{
if (string.IsNullOrEmpty(col.DbUpdateValue) == false)
{
sb.Append(_pgsqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ").Append(col.DbUpdateValue);
}
else
{
var caseWhen = _pgsqlUpdate.InternalWhereCaseSource(col.CsName, sqlval => sqlval).Trim();
sb.Append(caseWhen);
if (caseWhen.EndsWith(" END")) _pgsqlUpdate.InternalToSqlCaseWhenEnd(sb, col);
}
}
else
{
var field = _pgsqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name);
sb.Append(field).Append(" = EXCLUDED.").Append(field);
}
++colidx;
}
}
}
return sb.ToString();
}
public long ExecuteAffrows()
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
var before = new CurdBeforeEventArgs(_pgsqlInsert.InternalTable.Type, _pgsqlInsert.InternalTable, CurdType.Insert, sql, _pgsqlInsert.InternalParams);
_pgsqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_pgsqlInsert, before);
long ret = 0;
Exception exception = null;
try
{
ret = _pgsqlInsert.InternalOrm.Ado.ExecuteNonQuery(_pgsqlInsert.InternalConnection, _pgsqlInsert.InternalTransaction, CommandType.Text, sql, _pgsqlInsert._commandTimeout, _pgsqlInsert.InternalParams);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new CurdAfterEventArgs(before, exception, ret);
_pgsqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_pgsqlInsert, after);
ClearData();
}
return ret;
}
#if net40
#else
async public Task<long> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
var before = new CurdBeforeEventArgs(_pgsqlInsert.InternalTable.Type, _pgsqlInsert.InternalTable, CurdType.Insert, sql, _pgsqlInsert.InternalParams);
_pgsqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_pgsqlInsert, before);
long ret = 0;
Exception exception = null;
try
{
ret = await _pgsqlInsert.InternalOrm.Ado.ExecuteNonQueryAsync(_pgsqlInsert.InternalConnection, _pgsqlInsert.InternalTransaction, CommandType.Text, sql, _pgsqlInsert._commandTimeout, _pgsqlInsert.InternalParams, cancellationToken);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new CurdAfterEventArgs(before, exception, ret);
_pgsqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_pgsqlInsert, after);
ClearData();
}
return ret;
}
#endif
}
}

View File

@ -0,0 +1,32 @@
using FreeSql.Internal;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql.PostgreSQL.Curd
{
class QuestDbDelete<T1> : Internal.CommonProvider.DeleteProvider<T1>
{
public QuestDbDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
: base(orm, commonUtils, commonExpression, dywhere)
{
}
public override List<T1> ExecuteDeleted() => throw new NotImplementedException("QuestDb 不支持删除数据.");
public override int ExecuteAffrows() => throw new NotImplementedException("QuestDb 不支持删除数据.");
#if net40
#else
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("QuestDb 不支持删除数据.");
public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("QuestDb 不支持删除数据.");
#endif
}
}

View File

@ -0,0 +1,217 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql.QuestDb.Curd
{
class QuestDbInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
{
public QuestDbInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
: base(orm, commonUtils, commonExpression)
{
}
internal IFreeSql InternalOrm => _orm;
internal TableInfo InternalTable => _table;
internal DbParameter[] InternalParams => _params;
internal DbConnection InternalConnection => _connection;
internal DbTransaction InternalTransaction => _transaction;
internal CommonUtils InternalCommonUtils => _commonUtils;
internal CommonExpression InternalCommonExpression => _commonExpression;
internal List<T1> InternalSource => _source;
internal Dictionary<string, bool> InternalIgnore => _ignore;
internal void InternalClearData() => ClearData();
public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
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, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
try
{
ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return 0;
}
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
try
{
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params)), out ret);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return ret;
}
protected override List<T1> RawExecuteInserted()
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return new List<T1>();
var sb = new StringBuilder();
sb.Append(sql).Append(" RETURNING ");
var colidx = 0;
foreach (var col in _table.Columns.Values)
{
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
sql = sb.ToString();
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
var ret = new List<T1>();
Exception exception = null;
try
{
ret = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return ret;
}
#if net40
#else
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
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, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
try
{
ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return 0;
}
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
try
{
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken)), out ret);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return ret;
}
async protected override Task<List<T1>> RawExecuteInsertedAsync(CancellationToken cancellationToken = default)
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return new List<T1>();
var sb = new StringBuilder();
sb.Append(sql).Append(" RETURNING ");
var colidx = 0;
foreach (var col in _table.Columns.Values)
{
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
sql = sb.ToString();
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
var ret = new List<T1>();
Exception exception = null;
try
{
ret = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return ret;
}
#endif
}
}

View File

@ -0,0 +1,72 @@
using FreeSql.Internal;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
namespace FreeSql.QuestDb.Curd
{
class QuestDbInsertOrUpdate<T1> : Internal.CommonProvider.InsertOrUpdateProvider<T1> where T1 : class
{
public QuestDbInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
: base(orm, commonUtils, commonExpression)
{
}
public override string ToSql()
{
var dbParams = new List<DbParameter>();
if (_sourceSql != null)
{
var data = new List<T1>();
data.Add((T1)_table.Type.CreateInstanceGetDefaultValue());
var sql = getInsertSql(data, false, false);
var sb = new StringBuilder();
sb.Append(sql.Substring(0, sql.IndexOf(") VALUES")));
sb.Append(") \r\n");
WriteSourceSelectUnionAll(null, sb, null);
sb.Append(sql.Substring(sql.IndexOf("\r\nON CONFLICT(") + 2));
return sb.ToString();
}
if (_source?.Any() != true) return null;
var sqls = new string[2];
var ds = SplitSourceByIdentityValueIsNull(_source);
if (ds.Item1.Any()) sqls[0] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item1.Select(a => getInsertSql(a, false, true)));
if (ds.Item2.Any()) sqls[1] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item2.Select(a => getInsertSql(a, true, true)));
_params = dbParams.ToArray();
if (ds.Item2.Any() == false) return sqls[0];
if (ds.Item1.Any() == false) return sqls[1];
return string.Join("\r\n\r\n;\r\n\r\n", sqls);
string getInsertSql(List<T1> data, bool flagInsert, bool noneParameter)
{
var insert = _orm.Insert<T1>()
.AsTable(_tableRule).AsType(_table.Type)
.WithConnection(_connection)
.WithTransaction(_transaction)
.NoneParameter(noneParameter) as Internal.CommonProvider.InsertProvider<T1>;
insert._source = data;
insert._table = _table;
insert._noneParameterFlag = flagInsert ? "cuc" : "cu";
string sql = "";
if (IdentityColumn != null && flagInsert) sql = insert.ToSql();
else
{
var ocdu = new OnConflictDoUpdate<T1>(insert.InsertIdentity());
ocdu._tempPrimarys = _tempPrimarys;
var cols = _table.Columns.Values.Where(a => _tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && _updateIgnore.ContainsKey(a.Attribute.Name) == false);
ocdu.UpdateColumns(cols.Select(a => a.Attribute.Name).ToArray());
if (_doNothing == true || cols.Any() == false)
ocdu.DoNothing();
sql = ocdu.ToSql();
}
if (string.IsNullOrEmpty(sql)) return null;
if (insert._params?.Any() == true) dbParams.AddRange(insert._params);
return sql;
}
}
}
}

View File

@ -0,0 +1,645 @@
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;
using System.Threading;
namespace FreeSql.QuestDb.Curd
{
class QuestDbSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1>
{
internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select,
bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having,
string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables,
List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent,
List<GlobalFilter.Item> _whereGlobalFilter, IFreeSql _orm)
{
if (_orm.CodeFirst.IsAutoSyncStructure)
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
if (_whereGlobalFilter.Any())
foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true);
var sb = new StringBuilder();
var tbUnionsGt0 = tbUnions.Count > 1;
for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
{
if (tbUnionsIdx > 0) sb.Append("\r\n \r\nUNION ALL\r\n \r\n");
if (tbUnionsGt0) sb.Append(_select).Append(" * from (");
var tbUnion = tbUnions[tbUnionsIdx];
var sbnav = new StringBuilder();
sb.Append(_select);
if (_distinct) sb.Append("DISTINCT ");
sb.Append(field).Append(" \r\nFROM ");
var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
for (var a = 0; a < tbsfrom.Length; a++)
{
sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ")
.Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias);
if (tbsjoin.Length > 0)
{
//如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
for (var b = 1; b < tbsfrom.Length; b++)
{
sb.Append(" \r\nLEFT JOIN ")
.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ")
.Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ??
tbsfrom[b].Alias);
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) &&
string.IsNullOrEmpty(tbsfrom[b].On) &&
string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
else
{
var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
sb.Append(" ON ").Append(onSql);
if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
{
if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade);
else sb.Append(" AND ").Append(tbsfrom[b].Cascade);
}
}
}
break;
}
else
{
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition))
sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
if (!string.IsNullOrEmpty(tbsfrom[a].On))
sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade))
sbnav.Append(" AND ").Append(tbsfrom[a].Cascade);
}
if (a < tbsfrom.Length - 1) sb.Append(", ");
}
foreach (var tb in tbsjoin)
{
switch (tb.Type)
{
case SelectTableInfoType.Parent:
case SelectTableInfoType.RawJoin:
continue;
case SelectTableInfoType.LeftJoin:
sb.Append(" \r\nLEFT JOIN ");
break;
case SelectTableInfoType.InnerJoin:
sb.Append(" \r\nINNER JOIN ");
break;
case SelectTableInfoType.RightJoin:
sb.Append(" \r\nRIGHT JOIN ");
break;
}
sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ")
.Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ")
.Append(tb.On ?? tb.NavigateCondition);
if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade);
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition))
sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
}
if (_join.Length > 0) sb.Append(_join);
sbnav.Append(_where);
if (!string.IsNullOrEmpty(_tables[0].Cascade))
sbnav.Append(" AND ").Append(_tables[0].Cascade);
if (sbnav.Length > 0)
{
sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
}
if (string.IsNullOrEmpty(_groupby) == false)
{
//sb.Append(_groupby);
if (string.IsNullOrEmpty(_having) == false)
{
var message = @"提示由于QuedtDb语法不支持Having请使用以下语法代替
fsql.Select<Model>()
//.GroupBy(a => a.Id)
//.Having(a => a.Count() > 1)
.WithTempQuery(a => new
{
a.Id,
count1 = SqlExt.Count(a.Id).ToValue()
})
.Where(a => a.count1 > 1)
.ToList();";
throw new NotImplementedException(message);
}
}
sb.Append(_orderby);
//SamoleBy扩展
if (SampleByExtension.IsExistence.Value)
{
sb.Append(SampleByExtension.SamoleByString.Value);
SampleByExtension.Initialize();
}
//LatestOn扩展
if (LatestOnExtension.IsExistence.Value)
{
sb.Append(LatestOnExtension.LatestOnString.Value);
LatestOnExtension.Initialize();
}
if (_limit > 0)
sb.Append(" \r\nlimit ").Append(_skip == 0 ? _limit : _skip);
if (_skip > 0)
//sb.Append(" \r\noffset ").Append(_skip);
sb.Append(",").Append(_skip + _limit);
sbnav.Clear();
if (tbUnionsGt0) sb.Append(") ftb");
}
return sb.Append(_tosqlAppendContent).ToString();
}
public QuestDbSelect(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 QuestDbSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression,
null);
QuestDbSelect<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 QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm,
_commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> From<T2, T3, T4, T5,
T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm,
_commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> From<T2, T3, T4,
T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm,
_commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select,
_distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having,
_orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent,
_whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3>
where T2 : class where T3 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4>
where T2 : class where T3 : class where T4 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5>
where T2 : class where T3 : class where T4 : class where T5 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5,
T6> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4,
T5, T6, T7> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3,
T4, T5, T6, T7, T8> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2,
T3, T4, T5, T6, T7, T8, T9> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : FreeSql.Internal.CommonProvider.
Select11Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : FreeSql.Internal.CommonProvider.
Select12Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : FreeSql.Internal.CommonProvider.
Select13Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : FreeSql.Internal.
CommonProvider.Select14Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
where T14 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> : FreeSql.Internal.
CommonProvider.Select15Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
where T14 : class
where T15 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : FreeSql.Internal.
CommonProvider.Select16Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
where T14 : class
where T15 : class
where T16 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
}

View File

@ -0,0 +1,187 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql.QuestDb.Curd
{
class QuestDbUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1>
{
public QuestDbUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
: base(orm, commonUtils, commonExpression, dywhere)
{
}
internal string InternalTableAlias { get; set; }
internal StringBuilder InternalSbSet => _set;
internal StringBuilder InternalSbSetIncr => _setIncr;
internal Dictionary<string, bool> InternalIgnore => _ignore;
internal void InternalResetSource(List<T1> source) => _source = source;
internal string InternalWhereCaseSource(string CsName, Func<string, string> thenValue) => WhereCaseSource(CsName, thenValue);
internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col);
public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
protected override List<T1> RawExecuteUpdated()
{
var ret = new List<T1>();
DbParameter[] dbParms = null;
StringBuilder sbret = null;
ToSqlFetch(sb =>
{
if (dbParms == null)
{
dbParms = _params.Concat(_paramsSource).ToArray();
sbret = new StringBuilder();
sbret.Append(" RETURNING ");
var colidx = 0;
foreach (var col in _table.Columns.Values)
{
if (colidx > 0) sbret.Append(", ");
sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
}
var sql = sb.Append(sbret).ToString();
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
Exception exception = null;
try
{
var rettmp = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms);
ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
ret.AddRange(rettmp);
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
});
sbret?.Clear();
return ret;
}
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
{
if (primarys.Length == 1)
{
var pk = primarys.First();
if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append(".");
caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
return;
}
caseWhen.Append("(");
var pkidx = 0;
foreach (var pk in primarys)
{
if (pkidx > 0) caseWhen.Append(" || '+' || ");
if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append(".");
caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::text");
++pkidx;
}
caseWhen.Append(")");
}
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
{
if (primarys.Length == 1)
{
sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d)));
return;
}
sb.Append("(");
var pkidx = 0;
foreach (var pk in primarys)
{
if (pkidx > 0) sb.Append(" || '+' || ");
sb.Append(_commonUtils.FormatSql("{0}", pk.GetDbValue(d))).Append("::text");
++pkidx;
}
sb.Append(")");
}
protected override void ToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col)
{
if (_noneParameter == false) return;
if (col.Attribute.MapType == typeof(string))
{
sb.Append("::text");
return;
}
var dbtype = _commonUtils.CodeFirst.GetDbInfo(col.Attribute.MapType)?.dbtype;
if (dbtype == null) return;
sb.Append("::").Append(dbtype);
}
#if net40
#else
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
public override Task<List<T1>> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
async protected override Task<List<T1>> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default)
{
var ret = new List<T1>();
DbParameter[] dbParms = null;
StringBuilder sbret = null;
await ToSqlFetchAsync(async sb =>
{
if (dbParms == null)
{
dbParms = _params.Concat(_paramsSource).ToArray();
sbret = new StringBuilder();
sbret.Append(" RETURNING ");
var colidx = 0;
foreach (var col in _table.Columns.Values)
{
if (colidx > 0) sbret.Append(", ");
sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
}
var sql = sb.Append(sbret).ToString();
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
Exception exception = null;
try
{
var rettmp = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken);
ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
ret.AddRange(rettmp);
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
});
sbret?.Clear();
return ret;
}
#endif
}
}

View File

@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>YeXiangQin;Daily</Authors>
<Description>FreeSql适配QuestDb时序数据库访问</Description>
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>FreeSql;ORM;QuestDb</PackageTags>
<PackageId>$(AssemblyName)</PackageId>
<PackageIcon>logo.png</PackageIcon>
<Title>$(AssemblyName)</Title>
<IsPackable>true</IsPackable>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
<Version>3.2.688-preview20230216</Version>
</PropertyGroup>
<ItemGroup>
<None Include="../../logo.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Npgsql.LegacyPostgis" Version="5.0.11" />
<PackageReference Include="Npgsql.NetTopologySuite" Version="5.0.11" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="Npgsql.LegacyPostgis" Version="4.1.7" />
<PackageReference Include="Npgsql.NetTopologySuite" Version="4.1.7" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net452' or '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net45'">
<PackageReference Include="Npgsql.LegacyPostgis" Version="4.0.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net452' or '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net45'">
<DefineConstants>net45</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net461'">
<DefineConstants>nts</DefineConstants>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,109 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json.Linq;
using Npgsql;
using FreeSql.Internal.ObjectPool;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading;
namespace FreeSql.QuestDb
{
class QuestDbAdo : FreeSql.Internal.CommonProvider.AdoProvider
{
public QuestDbAdo() : base(DataType.PostgreSQL, null, null) { }
public QuestDbAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.PostgreSQL, masterConnectionString, slaveConnectionStrings)
{
base._util = util;
if (connectionFactory != null)
{
var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.PostgreSQL, connectionFactory);
ConnectionString = pool.TestConnection?.ConnectionString;
MasterPool = pool;
return;
}
if (!string.IsNullOrEmpty(masterConnectionString))
MasterPool = new QuestDbConnectionPool(CoreStrings.S_MasterDatabase, masterConnectionString, null, null);
if (slaveConnectionStrings != null)
{
foreach (var slaveConnectionString in slaveConnectionStrings)
{
var slavePool = new QuestDbConnectionPool($"{CoreStrings.S_SlaveDatabase}{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
SlavePools.Add(slavePool);
}
}
}
public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn)
{
if (param == null) return "NULL";
if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false || param is JToken || param is JObject || param is JArray))
param = Utils.GetDataReaderValue(mapType, param);
bool isdic;
if (param is bool || param is bool?)
return (bool)param;
else if (param is string)
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
else if (param is char)
return string.Concat("'", param.ToString().Replace("'", "''").Replace('\0', ' '), "'");
else if (param is Enum)
return ((Enum)param).ToInt64();
else if (decimal.TryParse(string.Concat(param), out var trydec))
return param;
else if (param is DateTime || param is DateTime?)
return string.Concat("'", ((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 byte[])
return $"'\\x{CommonUtils.BytesSqlRaw(param as byte[])}'";
else if (param is JToken || param is JObject || param is JArray)
return string.Concat("'", param.ToString().Replace("'", "''"), "'::jsonb");
else if ((isdic = param is Dictionary<string, string>) ||
param is IEnumerable<KeyValuePair<string, string>>)
{
var pgdics = isdic ? param as Dictionary<string, string> :
param as IEnumerable<KeyValuePair<string, string>>;
var pghstore = new StringBuilder("'");
var pairs = pgdics.ToArray();
for (var i = 0; i < pairs.Length; i++)
{
if (i != 0) pghstore.Append(",");
pghstore.AppendFormat("\"{0}\"=>", pairs[i].Key.Replace("'", "''"));
if (pairs[i].Value == null)
pghstore.Append("NULL");
else
pghstore.AppendFormat("\"{0}\"", pairs[i].Value.Replace("'", "''"));
}
return pghstore.Append("'::hstore");
}
else if (param is IEnumerable)
return AddslashesIEnumerable(param, mapType, mapColumn);
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
}
public override DbCommand CreateCommand()
{
return new NpgsqlCommand();
}
public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
{
var rawPool = pool as QuestDbConnectionPool;
if (rawPool != null) rawPool.Return(conn, ex);
else pool.Return(conn);
}
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
}
}

View File

@ -0,0 +1,241 @@
using Npgsql;
using FreeSql.Internal.ObjectPool;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace FreeSql.QuestDb
{
class QuestDbConnectionPool : ObjectPool<DbConnection>
{
internal Action availableHandler;
internal Action unavailableHandler;
public QuestDbConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null)
{
this.availableHandler = availableHandler;
this.unavailableHandler = unavailableHandler;
var policy = new PostgreSQLConnectionPoolPolicy
{
_pool = this,
Name = name
};
this.Policy = policy;
policy.ConnectionString = connectionString;
}
public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false)
{
if (exception != null && exception is NpgsqlException)
{
if (obj.Value.Ping() == false)
base.SetUnavailable(exception, obj.LastGetTimeCopy);
}
base.Return(obj, isRecreate);
}
}
class PostgreSQLConnectionPoolPolicy : IPolicy<DbConnection>
{
internal QuestDbConnectionPool _pool;
public string Name { get; set; } = $"PostgreSQL NpgsqlConnection {CoreStrings.S_ObjectPool}";
public int PoolSize { get; set; } = 50;
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(20);
public int AsyncGetCapacity { get; set; } = 10000;
public bool IsThrowGetTimeoutException { get; set; } = true;
public bool IsAutoDisposeWithSystem { get; set; } = true;
public int CheckAvailableInterval { get; set; } = 2;
public int Weight { get; set; } = 1;
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
private string _connectionString;
public string ConnectionString
{
get => _connectionString;
set
{
_connectionString = value ?? "";
var minPoolSize = 0;
var pattern = @"Min(imum)?\s*pool\s*size\s*=\s*(\d+)";
var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
if (m.Success)
{
minPoolSize = int.Parse(m.Groups[2].Value);
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
}
pattern = @"Max(imum)?\s*pool\s*size\s*=\s*(\d+)";
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
if (m.Success == false || int.TryParse(m.Groups[2].Value, out var poolsize) == false || poolsize <= 0) poolsize = Math.Max(50, minPoolSize);
var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => Math.Min(5, 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);
}
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
}
}
public bool OnCheckAvailable(Object<DbConnection> obj)
{
if (obj.Value == null) return false;
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
return obj.Value.Ping(true);
}
public DbConnection OnCreate()
{
var conn = new NpgsqlConnection(_connectionString);
return conn;
}
public void OnDestroy(DbConnection obj)
{
try { if (obj.State != ConnectionState.Closed) obj.Close(); } catch { }
try { NpgsqlConnection.ClearPool(obj as NpgsqlConnection); } catch { }
obj.Dispose();
}
public void OnGet(Object<DbConnection> obj)
{
if (_pool.IsAvailable)
{
if (obj.Value == null)
{
_pool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy);
throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
}
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, obj.LastGetTimeCopy) == true)
throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}");
throw ex;
}
}
}
}
#if net40
#else
async public Task OnGetAsync(Object<DbConnection> obj)
{
if (_pool.IsAvailable)
{
if (obj.Value == null)
{
_pool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy);
throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
}
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, obj.LastGetTimeCopy) == true)
throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}");
throw ex;
}
}
}
}
#endif
public void OnGetTimeout()
{
}
public void OnReturn(Object<DbConnection> obj)
{
//if (obj?.Value != null && 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;
}
}
#if net40
#else
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;
}
}
#endif
}
}

View File

@ -0,0 +1,157 @@
using Newtonsoft.Json.Linq;
using Npgsql;
using NpgsqlTypes;
using System;
using System.Collections;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
namespace Newtonsoft.Json
{
public class QuestDbTypesConverter : JsonConverter
{
private static readonly Type typeof_BitArray = typeof(BitArray);
private static readonly Type typeof_NpgsqlPoint = typeof(NpgsqlPoint);
private static readonly Type typeof_NpgsqlLine = typeof(NpgsqlLine);
private static readonly Type typeof_NpgsqlLSeg = typeof(NpgsqlLSeg);
private static readonly Type typeof_NpgsqlBox = typeof(NpgsqlBox);
private static readonly Type typeof_NpgsqlPath = typeof(NpgsqlPath);
private static readonly Type typeof_NpgsqlPolygon = typeof(NpgsqlPolygon);
private static readonly Type typeof_NpgsqlCircle = typeof(NpgsqlCircle);
private static readonly Type typeof_Cidr = typeof((IPAddress, int));
private static readonly Type typeof_IPAddress = typeof(IPAddress);
private static readonly Type typeof_PhysicalAddress = typeof(PhysicalAddress);
private static readonly Type typeof_String = typeof(string);
private static readonly Type typeof_NpgsqlRange_int = typeof(NpgsqlRange<int>);
private static readonly Type typeof_NpgsqlRange_long = typeof(NpgsqlRange<long>);
private static readonly Type typeof_NpgsqlRange_decimal = typeof(NpgsqlRange<decimal>);
private static readonly Type typeof_NpgsqlRange_DateTime = typeof(NpgsqlRange<DateTime>);
public override bool CanConvert(Type objectType)
{
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();
if (ctype == typeof_BitArray) return true;
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return true;
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return true;
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return true;
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return true;
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return true;
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return true;
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return true;
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) return true;
if (ctype == typeof_IPAddress) return true;
if (ctype == typeof_PhysicalAddress) return true;
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return true;
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return true;
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return true;
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return true;
return false;
}
private object YieldJToken(Type ctype, JToken jt, int rank)
{
if (jt.Type == JTokenType.Null) return null;
if (rank == 0)
{
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();//ctype.Namespace == "System" && ctype.Name.StartsWith("Nullable`") ? ctype.GenericTypeArguments.FirstOrDefault() : null;
if (ctype == typeof_BitArray) return jt.ToString().ToBitArray();
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return NpgsqlPoint.Parse(jt.ToString());
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return NpgsqlLine.Parse(jt.ToString());
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return NpgsqlLSeg.Parse(jt.ToString());
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return NpgsqlBox.Parse(jt.ToString());
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return NpgsqlPath.Parse(jt.ToString());
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return NpgsqlPolygon.Parse(jt.ToString());
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return NpgsqlCircle.Parse(jt.ToString());
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr)
{
var cidrArgs = jt.ToString().Split(new[] { '/' }, 2);
return (IPAddress.Parse(cidrArgs.First()), cidrArgs.Length >= 2 ? int.TryParse(cidrArgs[1], out var tryCdirSubnet) ? tryCdirSubnet : 0 : 0);
}
if (ctype == typeof_IPAddress) return IPAddress.Parse(jt.ToString());
if (ctype == typeof_PhysicalAddress) return PhysicalAddress.Parse(jt.ToString());
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return jt.ToString().ToNpgsqlRange<int>();
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return jt.ToString().ToNpgsqlRange<long>();
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return jt.ToString().ToNpgsqlRange<decimal>();
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return jt.ToString().ToNpgsqlRange<DateTime>();
return null;
}
var jtarr = jt.ToArray();
var ret = Array.CreateInstance(ctype, jtarr.Length);
var jtarrIdx = 0;
foreach (var a in jtarr)
{
var t2 = YieldJToken(ctype, a, rank - 1);
ret.SetValue(t2, jtarrIdx++);
}
return ret;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
int rank = objectType.IsArray ? objectType.GetArrayRank() : 0;
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
var ret = YieldJToken(ctype, JToken.Load(reader), rank);
if (ret != null && rank > 0) return ret;
return ret;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Type objectType = value.GetType();
if (objectType.IsArray)
{
int rank = objectType.GetArrayRank();
int[] indices = new int[rank];
GetJObject(value as Array, indices, 0).WriteTo(writer);
}
else
GetJObject(value).WriteTo(writer);
}
public static JToken GetJObject(object value)
{
if (value is BitArray) return JToken.FromObject((value as BitArray)?.To1010());
if (value is IPAddress) return JToken.FromObject((value as IPAddress)?.ToString());
if (value is ValueTuple<IPAddress, int> || value is ValueTuple<IPAddress, int>?)
{
ValueTuple<IPAddress, int>? cidrValue = (ValueTuple<IPAddress, int>?)value;
return JToken.FromObject(cidrValue == null ? null : $"{cidrValue.Value.Item1.ToString()}/{cidrValue.Value.Item2.ToString()}");
}
return JToken.FromObject(value?.ToString());
}
public static JToken GetJObject(Array value, int[] indices, int idx)
{
if (idx == indices.Length)
{
return GetJObject(value.GetValue(indices));
}
JArray ja = new JArray();
if (indices.Length == 1)
{
foreach (object a in value)
ja.Add(GetJObject(a));
return ja;
}
int lb = value.GetLowerBound(idx);
int ub = value.GetUpperBound(idx);
for (int b = lb; b <= ub; b++)
{
indices[idx] = b;
ja.Add(GetJObject(value, indices, idx + 1));
}
return ja;
}
}
}

View File

@ -0,0 +1,73 @@
using NpgsqlTypes;
using System;
using System.Collections;
public static partial class QuestDbTypesExtensions
{
/// <summary>
/// 测量两个经纬度的距离,返回单位:米
/// </summary>
/// <param name="that">经纬坐标1</param>
/// <param name="point">经纬坐标2</param>
/// <returns>返回距离(单位:米)</returns>
public static double Distance(this NpgsqlPoint that, NpgsqlPoint point)
{
double radLat1 = (double)(that.Y) * Math.PI / 180d;
double radLng1 = (double)(that.X) * Math.PI / 180d;
double radLat2 = (double)(point.Y) * Math.PI / 180d;
double radLng2 = (double)(point.X) * Math.PI / 180d;
return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
}
#if net40
#else
/// <summary>
/// 测量两个经纬度的距离,返回单位:米
/// </summary>
/// <param name="that">经纬坐标1</param>
/// <param name="point">经纬坐标2</param>
/// <returns>返回距离(单位:米)</returns>
public static double Distance(this Npgsql.LegacyPostgis.PostgisPoint that, Npgsql.LegacyPostgis.PostgisPoint point)
{
double radLat1 = (double)(that.Y) * Math.PI / 180d;
double radLng1 = (double)(that.X) * Math.PI / 180d;
double radLat2 = (double)(point.Y) * Math.PI / 180d;
double radLng2 = (double)(point.X) * Math.PI / 180d;
return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
}
public static NpgsqlRange<T> ToNpgsqlRange<T>(this string that)
{
var s = that;
if (string.IsNullOrEmpty(s) || s == "empty") return NpgsqlRange<T>.Empty;
string s1 = s.Trim('(', ')', '[', ']');
string[] ss = s1.Split(new char[] { ',' }, 2);
if (ss.Length != 2) return NpgsqlRange<T>.Empty;
T t1 = default(T);
T t2 = default(T);
if (!string.IsNullOrEmpty(ss[0])) t1 = (T)Convert.ChangeType(ss[0], typeof(T));
if (!string.IsNullOrEmpty(ss[1])) t2 = (T)Convert.ChangeType(ss[1], typeof(T));
return new NpgsqlRange<T>(t1, s[0] == '[', s[0] == '(', t2, s[s.Length - 1] == ']', s[s.Length - 1] == ')');
}
#endif
public static string To1010(this BitArray ba)
{
char[] ret = new char[ba.Length];
for (int a = 0; a < ba.Length; a++) ret[a] = ba[a] ? '1' : '0';
return new string(ret);
}
/// <summary>
/// 将 1010101010 这样的二进制字符串转换成 BitArray
/// </summary>
/// <param name="_1010Str">1010101010</param>
/// <returns></returns>
public static BitArray ToBitArray(this string _1010Str)
{
if (_1010Str == null) return null;
BitArray ret = new BitArray(_1010Str.Length);
for (int a = 0; a < _1010Str.Length; a++) ret[a] = _1010Str[a] == '1';
return ret;
}
}

View File

@ -0,0 +1,278 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using FreeSql.Provider.QuestDb.Subtable;
using Newtonsoft.Json.Linq;
using Npgsql.LegacyPostgis;
using NpgsqlTypes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
namespace FreeSql.QuestDb
{
class QuestDbCodeFirst : Internal.CommonProvider.CodeFirstProvider
{
public QuestDbCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm,
commonUtils, commonExpression)
{
}
static object _dicCsToDbLock = new object();
static Dictionary<string, CsToDb<NpgsqlDbType>> _dicCsToDb = new Dictionary<string, CsToDb<NpgsqlDbType>>()
{
{ typeof(sbyte).FullName, CsToDb.New(NpgsqlDbType.Smallint, "byte", "byte NOT NULL", false, false, 0) },
{ typeof(sbyte?).FullName, CsToDb.New(NpgsqlDbType.Smallint, "byte", "byte", false, true, null) },
{ typeof(short).FullName, CsToDb.New(NpgsqlDbType.Smallint, "short", "short NOT NULL", false, false, 0) },
{ typeof(short?).FullName, CsToDb.New(NpgsqlDbType.Smallint, "short", "short", false, true, null) },
{ typeof(int).FullName, CsToDb.New(NpgsqlDbType.Integer, "int", "int NOT NULL", false, false, 0) },
{ typeof(int?).FullName, CsToDb.New(NpgsqlDbType.Integer, "int", "int", false, true, null) },
{ typeof(long).FullName, CsToDb.New(NpgsqlDbType.Bigint, "long", "long NOT NULL", false, false, 0) },
{ typeof(long?).FullName, CsToDb.New(NpgsqlDbType.Bigint, "long", "long", false, true, null) },
{ typeof(byte).FullName, CsToDb.New(NpgsqlDbType.Smallint, "byte", "byte NOT NULL", false, false, 0) },
{ typeof(byte?).FullName, CsToDb.New(NpgsqlDbType.Smallint, "byte", "byte", false, true, null) },
{ typeof(ushort).FullName, CsToDb.New(NpgsqlDbType.Integer, "short", "short NOT NULL", false, false, 0) },
{ typeof(ushort?).FullName, CsToDb.New(NpgsqlDbType.Integer, "short", "short", false, true, null) },
{ typeof(uint).FullName, CsToDb.New(NpgsqlDbType.Bigint, "int", "int NOT NULL", false, false, 0) },
{ typeof(uint?).FullName, CsToDb.New(NpgsqlDbType.Bigint, "int", "int", false, true, null) },
{
typeof(ulong).FullName, CsToDb.New(NpgsqlDbType.Numeric, "long", "long NOT NULL", false, false, 0)
},
{
typeof(ulong?).FullName, CsToDb.New(NpgsqlDbType.Numeric, "long", "long", false, true, null)
},
{ typeof(float).FullName, CsToDb.New(NpgsqlDbType.Real, "float", "float NOT NULL", false, false, 0) },
{ typeof(float?).FullName, CsToDb.New(NpgsqlDbType.Real, "float", "float", false, true, null) },
{ typeof(double).FullName, CsToDb.New(NpgsqlDbType.Double, "double", "double NOT NULL", false, false, 0) },
{ typeof(double?).FullName, CsToDb.New(NpgsqlDbType.Double, "double", "double", false, true, null) },
{
typeof(decimal).FullName,
CsToDb.New(NpgsqlDbType.Numeric, "double", "double NOT NULL", false, false, 0)
},
{
typeof(decimal?).FullName,
CsToDb.New(NpgsqlDbType.Numeric, "double", "double", false, true, null)
},
{ typeof(string).FullName, CsToDb.New(NpgsqlDbType.Varchar, "string", "string", false, null, "") },
{ typeof(char).FullName, CsToDb.New(NpgsqlDbType.Char, "char", "char)", false, null, '\0') },
{
typeof(TimeSpan).FullName,
CsToDb.New(NpgsqlDbType.Time, "timestamp", "timestamp NOT NULL", false, false, 0)
},
{ typeof(TimeSpan?).FullName, CsToDb.New(NpgsqlDbType.Time, "timestamp", "timestamp", false, true, null) },
{
typeof(DateTime).FullName,
CsToDb.New(NpgsqlDbType.Timestamp, "timestamp", "timestamp NOT NULL", false, false,
new DateTime(1970, 1, 1))
},
{
typeof(DateTime?).FullName,
CsToDb.New(NpgsqlDbType.Timestamp, "timestamp", "timestamp", false, true, null)
},
{
typeof(bool).FullName,
CsToDb.New(NpgsqlDbType.Boolean, "boolean", "boolean NOT NULL", null, false, false)
},
{ typeof(bool?).FullName, CsToDb.New(NpgsqlDbType.Boolean, "boolean", "boolean", null, true, null) },
//{ typeof(Byte[]).FullName, CsToDb.New(NpgsqlDbType.Bytea, "bytea", "bytea", false, null, new byte[0]) },
//{
// typeof(BitArray).FullName,
// CsToDb.New(NpgsqlDbType.Varbit, "varbit", "varbit(64)", false, null, new BitArray(new byte[64]))
//},
{
typeof(BigInteger).FullName,
CsToDb.New(NpgsqlDbType.Numeric, "long", "long NOT NULL", false, false, 0)
},
{
typeof(BigInteger?).FullName,
CsToDb.New(NpgsqlDbType.Numeric, "long", "long", false, true, null)
}
};
public override DbInfoResult GetDbInfo(Type type)
{
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc))
return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable,
trydc.defaultValue);
if (type.IsArray)
return null;
return null;
}
protected override string GetComparisonDDLStatements(params TypeAndName[] objects)
{
var sb = new StringBuilder();
var seqcols = new List<NativeTuple<ColumnInfo, string[], bool>>(); //序列
foreach (var obj in objects)
{
if (sb.Length > 0) sb.Append("\r\n");
var tb = _commonUtils.GetTableByEntity(obj.entityType);
if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName));
if (tb.Columns.Any() == false)
throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName));
var tbnameArray = _commonUtils.SplitTableName(tb.DbName);
var tbname = string.Empty;
if (tbnameArray?.Length == 1) tbname = tbnameArray.FirstOrDefault();
var tboldnameArray = _commonUtils.SplitTableName(tb.DbOldName);
var tboldname = string.Empty;
if (tboldnameArray?.Length == 1)
tboldname = tboldnameArray.FirstOrDefault();
var sbalter = new StringBuilder();
var allTable = _orm.Ado.Query<string>(CommandType.Text,
@"SHOW TABLES");
//如果旧表名和现表名均不存在,则直接创建表
if (string.IsNullOrWhiteSpace(tboldname) && allTable.Any(s => s.Equals(tbname)) == false)
{
//创建表
var createTableName = _commonUtils.QuoteSqlName(tbname);
sbalter.Append("CREATE TABLE ").Append(createTableName).Append(" ( ");
foreach (var tbcol in tb.ColumnsByPosition)
{
sbalter.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ")
.Append(tbcol.Attribute.DbType);
if (tbcol.Attribute.IsIdentity == true)
{
//No IsIdentity
}
sbalter.Append(",");
}
sbalter.Remove(sbalter.Length - 1, 1);
sbalter.Append(") ");
if (tb.Indexes.Any())
{
sbalter.Append(",\r\n");
}
//创建表的索引
foreach (var uk in tb.Indexes)
{
sbalter.Append($"INDEX (");
foreach (var tbcol in uk.Columns)
{
if (tbcol.Column.Attribute.DbType != "SYMBOL")
throw new Exception("索引只能是string类型且[Column(DbType = \"symbol\")]");
sbalter.Append($"{_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)}");
}
sbalter.Append($"),");
}
sbalter.Remove(sbalter.Length - 1, 1);
//是否存在分表
foreach (var propety in obj.entityType.GetProperties())
{
var timeAttr = propety.GetCustomAttribute<AutoSubtableAttribute>();
if (timeAttr != null)
{
var colName = tb.Columns.FirstOrDefault(it => it.Key == propety.Name).Value;
sbalter.Append(
$" TIMESTAMP({colName.Attribute.Name}) PARTITION BY {timeAttr.SubtableType};{Environment.NewLine}");
}
}
}
//如果旧表名特性存在,旧表名在数据库中存在,现表名在数据库中不存在,直接走修改表名逻辑
else if (string.IsNullOrWhiteSpace(tboldname) == false && allTable.Any(s => s.Equals(tboldname)) &&
allTable.Any(s => s.Equals(tbname)) == false)
{
//修改表名
sbalter.Append("RENAME TABLE ")
.Append(_commonUtils.QuoteSqlName(tboldname))
.Append(" TO ").Append(_commonUtils.QuoteSqlName(tbname))
.Append($";{Environment.NewLine}");
}
//如果旧表名特性存在,旧表名在数据库中不存在,现表名在数据库中存在,对比列
//如果旧表名特性不存在 现表名在数据库中存在,对比列
else if ((string.IsNullOrWhiteSpace(tboldname) == false &&
allTable.Any(s => s.Equals(tboldname)) == false &&
allTable.Any(s => s.Equals(tbname)) == true)
|| (string.IsNullOrWhiteSpace(tboldname) == true &&
allTable.Any(s => s.Equals(tbname)) == true))
{
//查询列
var questDbColumnInfo = _orm.Ado.ExecuteArray($"SHOW COLUMNS FROM '{tbname}'").Select(o => new
{
columnName = o[0].ToString(),
indexed = Convert.ToBoolean(o[2])
}).ToList();
//对比列
foreach (var tbcol in tb.ColumnsByPosition)
{
//如果旧列名存在 现列名均不存在 直接添加列
if (questDbColumnInfo.Any(a => a.columnName.Equals(tbcol.Attribute.OldName)) == false
&& questDbColumnInfo.Any(a => a.columnName.Equals(tbcol.Attribute.Name)) ==
false)
{
sbalter.Append("ALTER TABLE ").Append(tbname)
.Append(" ADD COLUMN ").Append(tbcol.Attribute.Name).Append(" ")
.Append(tbcol.Attribute.DbType).Append($";{Environment.NewLine}");
questDbColumnInfo.Add(new
{
columnName = tbcol.Attribute.Name,
indexed = false
});
}
//如果旧列名存在,现列名不存在,直接修改列名
else if (questDbColumnInfo.Any(a =>
a.columnName.ToString().Equals(tbcol.Attribute.OldName)) == true
&& questDbColumnInfo.Any(a => a.columnName.ToString().Equals(tbcol.Attribute.Name)) ==
false)
{
sbalter.Append("ALTER TABLE ").Append(tbname)
.Append(" RENAME COLUMN ").Append(tbcol.Attribute.OldName).Append(" TO ")
.Append(tbcol.Attribute.Name).Append($";{Environment.NewLine}");
}
}
//对比索引
foreach (var uk in tb.Indexes)
{
if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false)
continue;
var ukname = ReplaceIndexName(uk.Name, tbname);
//先判断表中有没此字段的索引
var isIndex = questDbColumnInfo
.Where(a => a.columnName.ToString().Equals(uk.Columns.First().Column.Attribute.Name))
.FirstOrDefault()?.indexed;
//如果此字段不是索引
if (isIndex != null && isIndex == false)
{
//创建索引
sbalter.Append($"ALTER TABLE {tbname} ALTER COLUMN ");
foreach (var tbcol in uk.Columns)
{
if (tbcol.Column.Attribute.DbType != "SYMBOL")
throw new Exception("索引只能是string类型且[Column(DbType = \"symbol\")]");
sbalter.Append($"{tbcol.Column.Attribute.Name}");
}
sbalter.Append($" ADD INDEX;{Environment.NewLine}");
}
}
}
sb.Append(sbalter);
}
return sb.Length == 0 ? null : sb.ToString();
}
}
}

View File

@ -0,0 +1,783 @@
using FreeSql.DatabaseModel;
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json.Linq;
using Npgsql.LegacyPostgis;
using NpgsqlTypes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
namespace FreeSql.QuestDb
{
class QuestDbDbFirst : IDbFirst
{
IFreeSql _orm;
protected CommonUtils _commonUtils;
protected CommonExpression _commonExpression;
public QuestDbDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
{
_orm = orm;
_commonUtils = commonUtils;
_commonExpression = commonExpression;
}
public bool IsPg10 => ServerVersion >= 10;
public int ServerVersion
{
get
{
if (_ServerVersionValue == 0 && _orm.Ado.MasterPool != null)
using (var conn = _orm.Ado.MasterPool.Get())
{
try
{
_ServerVersionValue = ParsePgVersion(conn.Value.ServerVersion, 10, 0).Item2;
}
catch
{
_ServerVersionValue = 9;
}
}
return _ServerVersionValue;
}
}
int _ServerVersionValue = 0;
public int GetDbType(DbColumnInfo column) => (int)GetNpgsqlDbType(column);
NpgsqlDbType GetNpgsqlDbType(DbColumnInfo column)
{
var dbtype = column.DbTypeText;
var isarray = dbtype?.EndsWith("[]") == true;
if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2);
NpgsqlDbType ret = NpgsqlDbType.Unknown;
switch (dbtype?.ToLower().TrimStart('_'))
{
case "short":
ret = NpgsqlDbType.Smallint;
break;
case "int":
ret = NpgsqlDbType.Integer;
break;
case "long":
ret = NpgsqlDbType.Bigint;
break;
case "numeric":
ret = NpgsqlDbType.Numeric;
break;
case "float":
ret = NpgsqlDbType.Real;
break;
case "double":
ret = NpgsqlDbType.Double;
break;
case "char":
ret = NpgsqlDbType.Char;
break;
case "string":
ret = NpgsqlDbType.Varchar;
break;
case "timestamp":
ret = NpgsqlDbType.Timestamp;
break;
case "timestamptz":
ret = NpgsqlDbType.TimestampTz;
break;
case "date":
ret = NpgsqlDbType.Date;
break;
case "time":
ret = NpgsqlDbType.Time;
break;
case "timetz":
ret = NpgsqlDbType.TimeTz;
break;
case "interval":
ret = NpgsqlDbType.Interval;
break;
case "bool":
ret = NpgsqlDbType.Boolean;
break;
case "bytea":
ret = NpgsqlDbType.Bytea;
break;
case "bit":
ret = NpgsqlDbType.Bit;
break;
case "varbit":
ret = NpgsqlDbType.Varbit;
break;
case "point":
ret = NpgsqlDbType.Point;
break;
case "line":
ret = NpgsqlDbType.Line;
break;
case "lseg":
ret = NpgsqlDbType.LSeg;
break;
case "box":
ret = NpgsqlDbType.Box;
break;
case "path":
ret = NpgsqlDbType.Path;
break;
case "polygon":
ret = NpgsqlDbType.Polygon;
break;
case "circle":
ret = NpgsqlDbType.Circle;
break;
case "cidr":
ret = NpgsqlDbType.Cidr;
break;
case "inet":
ret = NpgsqlDbType.Inet;
break;
case "macaddr":
ret = NpgsqlDbType.MacAddr;
break;
case "json":
ret = NpgsqlDbType.Json;
break;
case "jsonb":
ret = NpgsqlDbType.Jsonb;
break;
case "uuid":
ret = NpgsqlDbType.Uuid;
break;
case "int4range":
ret = NpgsqlDbType.Range | NpgsqlDbType.Integer;
break;
case "int8range":
ret = NpgsqlDbType.Range | NpgsqlDbType.Bigint;
break;
case "numrange":
ret = NpgsqlDbType.Range | NpgsqlDbType.Numeric;
break;
case "tsrange":
ret = NpgsqlDbType.Range | NpgsqlDbType.Timestamp;
break;
case "tstzrange":
ret = NpgsqlDbType.Range | NpgsqlDbType.TimestampTz;
break;
case "daterange":
ret = NpgsqlDbType.Range | NpgsqlDbType.Date;
break;
case "hstore":
ret = NpgsqlDbType.Hstore;
break;
case "geometry":
ret = NpgsqlDbType.Geometry;
break;
}
return isarray ? (ret | NpgsqlDbType.Array) : 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)NpgsqlDbType.Smallint,
("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?),
"{0}.Value", "GetInt16")
},
{
(int)NpgsqlDbType.Integer,
("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value",
"GetInt32")
},
{
(int)NpgsqlDbType.Bigint,
("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?),
"{0}.Value", "GetInt64")
},
{
(int)NpgsqlDbType.Numeric,
("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal),
typeof(decimal?), "{0}.Value", "GetDecimal")
},
{
(int)NpgsqlDbType.Real,
("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?),
"{0}.Value", "GetFloat")
},
{
(int)NpgsqlDbType.Double,
("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?),
"{0}.Value", "GetDouble")
},
{
(int)NpgsqlDbType.Money,
("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal),
typeof(decimal?), "{0}.Value", "GetDecimal")
},
{
(int)NpgsqlDbType.Char,
("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string",
typeof(string), typeof(string), "{0}", "GetString")
},
{
(int)NpgsqlDbType.Varchar,
("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string",
typeof(string), typeof(string), "{0}", "GetString")
},
{
(int)NpgsqlDbType.Text,
("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string",
typeof(string), typeof(string), "{0}", "GetString")
},
{
(int)NpgsqlDbType.Timestamp,
("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?",
typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime")
},
{
(int)NpgsqlDbType.TimestampTz,
("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?",
typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime")
},
{
(int)NpgsqlDbType.Date,
("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?",
typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime")
},
{
(int)NpgsqlDbType.Time,
("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?",
typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue")
},
{
(int)NpgsqlDbType.TimeTz,
("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?",
typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue")
},
{
(int)NpgsqlDbType.Interval,
("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?",
typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue")
},
{
(int)NpgsqlDbType.Boolean,
("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?),
"{0}.Value", "GetBoolean")
},
{
(int)NpgsqlDbType.Bytea,
("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]",
typeof(byte[]), typeof(byte[]), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Bit,
("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray),
typeof(BitArray), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Varbit,
("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray),
typeof(BitArray), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Point,
("(NpgsqlPoint?)", "NpgsqlPoint.Parse({0})", "{0}.ToString()", "NpgsqlPoint",
typeof(NpgsqlPoint), typeof(NpgsqlPoint?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Line,
("(NpgsqlLine?)", "NpgsqlLine.Parse({0})", "{0}.ToString()", "NpgsqlLine", typeof(NpgsqlLine),
typeof(NpgsqlLine?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.LSeg,
("(NpgsqlLSeg?)", "NpgsqlLSeg.Parse({0})", "{0}.ToString()", "NpgsqlLSeg", typeof(NpgsqlLSeg),
typeof(NpgsqlLSeg?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Box,
("(NpgsqlBox?)", "NpgsqlBox.Parse({0})", "{0}.ToString()", "NpgsqlBox", typeof(NpgsqlBox),
typeof(NpgsqlBox?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Path,
("(NpgsqlPath?)", "NpgsqlPath.Parse({0})", "{0}.ToString()", "NpgsqlPath", typeof(NpgsqlPath),
typeof(NpgsqlPath?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Polygon,
("(NpgsqlPolygon?)", "NpgsqlPolygon.Parse({0})", "{0}.ToString()", "NpgsqlPolygon",
typeof(NpgsqlPolygon), typeof(NpgsqlPolygon?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Circle,
("(NpgsqlCircle?)", "NpgsqlCircle.Parse({0})", "{0}.ToString()", "NpgsqlCircle",
typeof(NpgsqlCircle), typeof(NpgsqlCircle?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Cidr,
("((IPAddress, int)?)", "(IPAddress, int)({0})", "{0}.ToString()", "(IPAddress, int)",
typeof((IPAddress, int)), typeof((IPAddress, int)?), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Inet,
("(IPAddress)", "IPAddress.Parse({0})", "{0}.ToString()", "IPAddress", typeof(IPAddress),
typeof(IPAddress), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.MacAddr,
("(PhysicalAddress?)", "PhysicalAddress.Parse({0})", "{0}.ToString()", "PhysicalAddress",
typeof(PhysicalAddress), typeof(PhysicalAddress), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Json,
("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken),
"{0}", "GetString")
},
{
(int)NpgsqlDbType.Jsonb,
("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken),
"{0}", "GetString")
},
{
(int)NpgsqlDbType.Uuid,
("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid", typeof(Guid), typeof(Guid?), "{0}",
"GetString")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Integer),
("(NpgsqlRange<int>?)", "{0}.ToNpgsqlRange<int>()", "{0}.ToString()", "NpgsqlRange<int>",
typeof(NpgsqlRange<int>), typeof(NpgsqlRange<int>?), "{0}", "GetString")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Bigint),
("(NpgsqlRange<long>?)", "{0}.ToNpgsqlRange<long>()", "{0}.ToString()", "NpgsqlRange<long>",
typeof(NpgsqlRange<long>), typeof(NpgsqlRange<long>?), "{0}", "GetString")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Numeric),
("(NpgsqlRange<decimal>?)", "{0}.ToNpgsqlRange<decimal>()", "{0}.ToString()",
"NpgsqlRange<decimal>", typeof(NpgsqlRange<decimal>), typeof(NpgsqlRange<decimal>?), "{0}",
"GetString")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Timestamp),
("(NpgsqlRange<DateTime>?)", "{0}.ToNpgsqlRange<DateTime>()", "{0}.ToString()",
"NpgsqlRange<DateTime>", typeof(NpgsqlRange<DateTime>), typeof(NpgsqlRange<DateTime>?),
"{0}", "GetString")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.TimestampTz),
("(NpgsqlRange<DateTime>?)", "{0}.ToNpgsqlRange<DateTime>()", "{0}.ToString()",
"NpgsqlRange<DateTime>", typeof(NpgsqlRange<DateTime>), typeof(NpgsqlRange<DateTime>?),
"{0}", "GetString")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Date),
("(NpgsqlRange<DateTime>?)", "{0}.ToNpgsqlRange<DateTime>()", "{0}.ToString()",
"NpgsqlRange<DateTime>", typeof(NpgsqlRange<DateTime>), typeof(NpgsqlRange<DateTime>?),
"{0}", "GetString")
},
{
(int)NpgsqlDbType.Hstore,
("(Dictionary<string, string>)",
"JsonConvert.DeserializeObject<Dictionary<string, string>>({0})",
"JsonConvert.SerializeObject({0})", "Dictionary<string, string>",
typeof(Dictionary<string, string>), typeof(Dictionary<string, string>), "{0}", "GetValue")
},
{
(int)NpgsqlDbType.Geometry,
("(PostgisGeometry)", "JsonConvert.DeserializeObject<PostgisGeometry>({0})",
"JsonConvert.SerializeObject({0})", "PostgisGeometry", typeof(PostgisGeometry),
typeof(PostgisGeometry), "{0}", "GetValue")
},
/*** array ***/
{
(int)(NpgsqlDbType.Smallint | NpgsqlDbType.Array),
("(short[])", "JsonConvert.DeserializeObject<short[]>({0})", "JsonConvert.SerializeObject({0})",
"short[]", typeof(short[]), typeof(short[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Integer | NpgsqlDbType.Array),
("(int[])", "JsonConvert.DeserializeObject<int[]>({0})", "JsonConvert.SerializeObject({0})",
"int[]", typeof(int[]), typeof(int[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Bigint | NpgsqlDbType.Array),
("(long[])", "JsonConvert.DeserializeObject<long[]>({0})", "JsonConvert.SerializeObject({0})",
"long[]", typeof(long[]), typeof(long[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Numeric | NpgsqlDbType.Array),
("(decimal[])", "JsonConvert.DeserializeObject<decimal[]>({0})",
"JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Real | NpgsqlDbType.Array),
("(float[])", "JsonConvert.DeserializeObject<float[]>({0})", "JsonConvert.SerializeObject({0})",
"float[]", typeof(float[]), typeof(float[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Double | NpgsqlDbType.Array),
("(double[])", "JsonConvert.DeserializeObject<double[]>({0})",
"JsonConvert.SerializeObject({0})", "double[]", typeof(double[]), typeof(double[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Money | NpgsqlDbType.Array),
("(decimal[])", "JsonConvert.DeserializeObject<decimal[]>({0})",
"JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Char | NpgsqlDbType.Array),
("(string[])", "JsonConvert.DeserializeObject<string[]>({0})",
"JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Varchar | NpgsqlDbType.Array),
("(string[])", "JsonConvert.DeserializeObject<string[]>({0})",
"JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Text | NpgsqlDbType.Array),
("(string[])", "JsonConvert.DeserializeObject<string[]>({0})",
"JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Timestamp | NpgsqlDbType.Array),
("(DateTime[])", "JsonConvert.DeserializeObject<DateTime[]>({0})",
"JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.TimestampTz | NpgsqlDbType.Array),
("(DateTime[])", "JsonConvert.DeserializeObject<DateTime[]>({0})",
"JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Date | NpgsqlDbType.Array),
("(DateTime[])", "JsonConvert.DeserializeObject<DateTime[]>({0})",
"JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Time | NpgsqlDbType.Array),
("(TimeSpan[])", "JsonConvert.DeserializeObject<TimeSpan[]>({0})",
"JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.TimeTz | NpgsqlDbType.Array),
("(TimeSpan[])", "JsonConvert.DeserializeObject<TimeSpan[]>({0})",
"JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Interval | NpgsqlDbType.Array),
("(TimeSpan[])", "JsonConvert.DeserializeObject<TimeSpan[]>({0})",
"JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Boolean | NpgsqlDbType.Array),
("(bool[])", "JsonConvert.DeserializeObject<bool[]>({0})", "JsonConvert.SerializeObject({0})",
"bool[]", typeof(bool[]), typeof(bool[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Bytea | NpgsqlDbType.Array),
("(byte[][])", "JsonConvert.DeserializeObject<byte[][]>({0})",
"JsonConvert.SerializeObject({0})", "byte[][]", typeof(byte[][]), typeof(byte[][]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Bit | NpgsqlDbType.Array),
("(BitArray[])", "JsonConvert.DeserializeObject<BitArray[]>({0})",
"JsonConvert.SerializeObject({0})", "BitArray[]", typeof(BitArray[]), typeof(BitArray[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Varbit | NpgsqlDbType.Array),
("(BitArray[])", "JsonConvert.DeserializeObject<BitArray[]>({0})",
"JsonConvert.SerializeObject({0})", "BitArray[]", typeof(BitArray[]), typeof(BitArray[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Point | NpgsqlDbType.Array),
("(NpgsqlPoint[])", "JsonConvert.DeserializeObject<NpgsqlPoint[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlPoint[]", typeof(NpgsqlPoint[]),
typeof(NpgsqlPoint[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Line | NpgsqlDbType.Array),
("(NpgsqlLine[])", "JsonConvert.DeserializeObject<BNpgsqlLineitArray[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlLine[]", typeof(NpgsqlLine[]),
typeof(NpgsqlLine[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.LSeg | NpgsqlDbType.Array),
("(NpgsqlLSeg[])", "JsonConvert.DeserializeObject<NpgsqlLSeg[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlLSeg[]", typeof(NpgsqlLSeg[]),
typeof(NpgsqlLSeg[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Box | NpgsqlDbType.Array),
("(NpgsqlBox[])", "JsonConvert.DeserializeObject<NpgsqlBox[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlBox[]", typeof(NpgsqlBox[]), typeof(NpgsqlBox[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Path | NpgsqlDbType.Array),
("(NpgsqlPath[])", "JsonConvert.DeserializeObject<NpgsqlPath[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlPath[]", typeof(NpgsqlPath[]),
typeof(NpgsqlPath[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Polygon | NpgsqlDbType.Array),
("(NpgsqlPolygon[])", "JsonConvert.DeserializeObject<NpgsqlPolygon[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlPolygon[]", typeof(NpgsqlPolygon[]),
typeof(NpgsqlPolygon[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Circle | NpgsqlDbType.Array),
("(NpgsqlCircle[])", "JsonConvert.DeserializeObject<NpgsqlCircle[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlCircle[]", typeof(NpgsqlCircle[]),
typeof(NpgsqlCircle[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Cidr | NpgsqlDbType.Array),
("((IPAddress, int)[])", "JsonConvert.DeserializeObject<(IPAddress, int)[]>({0})",
"JsonConvert.SerializeObject({0})", "(IPAddress, int)[]", typeof((IPAddress, int)[]),
typeof((IPAddress, int)[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Inet | NpgsqlDbType.Array),
("(IPAddress[])", "JsonConvert.DeserializeObject<IPAddress[]>({0})",
"JsonConvert.SerializeObject({0})", "IPAddress[]", typeof(IPAddress[]), typeof(IPAddress[]),
"{0}", "GetValue")
},
{
(int)(NpgsqlDbType.MacAddr | NpgsqlDbType.Array),
("(PhysicalAddress[])", "JsonConvert.DeserializeObject<PhysicalAddress[]>({0})",
"JsonConvert.SerializeObject({0})", "PhysicalAddress[]", typeof(PhysicalAddress[]),
typeof(PhysicalAddress[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Json | NpgsqlDbType.Array),
("(JToken[])", "JsonConvert.DeserializeObject<JToken[]>({0})",
"JsonConvert.SerializeObject({0})", "JToken[]", typeof(JToken[]), typeof(JToken[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Jsonb | NpgsqlDbType.Array),
("(JToken[])", "JsonConvert.DeserializeObject<JToken[]>({0})",
"JsonConvert.SerializeObject({0})", "JToken[]", typeof(JToken[]), typeof(JToken[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Uuid | NpgsqlDbType.Array),
("(Guid[])", "JsonConvert.DeserializeObject<Guid[]>({0})", "JsonConvert.SerializeObject({0})",
"Guid[]", typeof(Guid[]), typeof(Guid[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Integer | NpgsqlDbType.Array),
("(NpgsqlRange<int>[])", "JsonConvert.DeserializeObject<NpgsqlRange<int>[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlRange<int>[]", typeof(NpgsqlRange<int>[]),
typeof(NpgsqlRange<int>[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Bigint | NpgsqlDbType.Array),
("(NpgsqlRange<long>[])", "JsonConvert.DeserializeObject<NpgsqlRange<long>[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlRange<long>[]", typeof(NpgsqlRange<long>[]),
typeof(NpgsqlRange<long>[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Numeric | NpgsqlDbType.Array),
("(NpgsqlRange<decimal>[])", "JsonConvert.DeserializeObject<NpgsqlRange<decimal>[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlRange<decimal>[]",
typeof(NpgsqlRange<decimal>[]), typeof(NpgsqlRange<decimal>[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Timestamp | NpgsqlDbType.Array),
("(NpgsqlRange<DateTime>[])", "JsonConvert.DeserializeObject<NpgsqlRange<DateTime>[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlRange<DateTime>[]",
typeof(NpgsqlRange<DateTime>[]), typeof(NpgsqlRange<DateTime>[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.TimestampTz | NpgsqlDbType.Array),
("(NpgsqlRange<DateTime>[])", "JsonConvert.DeserializeObject<NpgsqlRange<DateTime>[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlRange<DateTime>[]",
typeof(NpgsqlRange<DateTime>[]), typeof(NpgsqlRange<DateTime>[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Range | NpgsqlDbType.Date | NpgsqlDbType.Array),
("(NpgsqlRange<DateTime>[])", "JsonConvert.DeserializeObject<NpgsqlRange<DateTime>[]>({0})",
"JsonConvert.SerializeObject({0})", "NpgsqlRange<DateTime>[]",
typeof(NpgsqlRange<DateTime>[]), typeof(NpgsqlRange<DateTime>[]), "{0}", "GetValue")
},
{
(int)(NpgsqlDbType.Hstore | NpgsqlDbType.Array),
("(Dictionary<string, string>[])",
"JsonConvert.DeserializeObject<Dictionary<string, string>[]>({0})",
"JsonConvert.SerializeObject({0})", "Dictionary<string, string>[]",
typeof(Dictionary<string, string>[]), typeof(Dictionary<string, string>[]), "{0}",
"GetValue")
},
{
(int)(NpgsqlDbType.Geometry | NpgsqlDbType.Array),
("(PostgisGeometry[])", "JsonConvert.DeserializeObject<PostgisGeometry[]>({0})",
"JsonConvert.SerializeObject({0})", "PostgisGeometry[]", typeof(PostgisGeometry[]),
typeof(PostgisGeometry[]), "{0}", "GetValue")
},
};
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()
{
throw new NotImplementedException();
}
public bool ExistsTable(string name, bool ignoreCase)
{
if (string.IsNullOrEmpty(name)) return false;
var tbnameArray = _commonUtils.SplitTableName(name);
var tbname = string.Empty;
if (tbnameArray?.Length == 1)
tbname = tbnameArray.FirstOrDefault();
var resList = _orm.Ado.Query<string>(CommandType.Text, @"SHOW TABLES");
var res = false;
if (ignoreCase)
res = resList.Any(s => s.ToLower().Equals(tbname.ToLower()));
else
res = resList.Any(s => s.Equals(tbname));
return res;
}
public DbTableInfo GetTableByName(string name, bool ignoreCase = true) =>
GetTables(null, name, ignoreCase)?.FirstOrDefault();
public List<DbTableInfo> GetTablesByDatabase(params string[] database) => GetTables(database, null, false);
public List<DbTableInfo> GetTables(string[] database, string tablename, bool ignoreCase)
{
var resList = _orm.Ado.Query<string>(CommandType.Text, @"SHOW TABLES");
var tables = new List<DbTableInfo>();
resList.ForEach(s =>
{
var tableColumns = _orm.Ado.ExecuteDataTable($"SHOW COLUMNS FROM '{s}'");
List<DbColumnInfo> dbColumnInfos = new List<DbColumnInfo>();
var dbTableInfo = new DbTableInfo()
{
Name = s,
Columns = new List<DbColumnInfo>()
};
foreach (DataRow tableColumnsRow in tableColumns.Rows)
{
dbColumnInfos.Add(new DbColumnInfo()
{
Name = tableColumnsRow["column"].ToString(),
DbTypeText = tableColumnsRow["type"].ToString(),
Table = dbTableInfo,
});
}
dbTableInfo.Columns = dbColumnInfos;
tables.Add(dbTableInfo);
});
return tables;
}
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
{
throw new NotImplementedException();
}
public static NativeTuple<bool, int, int> ParsePgVersion(string versionString, int v1, int v2)
{
int[] version = new int[] { 0, 0 };
var vmatch = Regex.Match(versionString, @"(\d+)\.(\d+)");
if (vmatch.Success)
{
version[0] = int.Parse(vmatch.Groups[1].Value);
version[1] = int.Parse(vmatch.Groups[2].Value);
}
else
{
vmatch = Regex.Match(versionString, @"(\d+)");
version[0] = int.Parse(vmatch.Groups[1].Value);
}
if (version[0] > v1)
return NativeTuple.Create(true, version[0], version[1]);
if (version[0] == v1 && version[1] >= v2)
return NativeTuple.Create(true, version[0], version[1]);
return NativeTuple.Create(false, version[0], version[1]);
}
}
}

View File

@ -0,0 +1,831 @@
using FreeSql.Internal;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
namespace FreeSql.QuestDb
{
class QuestDbExpression : CommonExpression
{
public QuestDbExpression(CommonUtils common) : base(common)
{
}
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc)
{
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
switch (exp.NodeType)
{
case ExpressionType.ArrayLength:
var arrOper = (exp as UnaryExpression)?.Operand;
var arrOperExp = getExp(arrOper);
if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")"))
return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)";
if (arrOper.Type == typeof(byte[])) return $"octet_length({getExp(arrOper)})";
return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end";
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 ExpressionConstDateTime(operandExp) ?? $"({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)})::text";
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.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 ExpressionConstDateTime(callExp.Arguments[0]) ??
$"({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":
return null;
case "Next":
if (callExp.Object?.Type == typeof(Random)) return "(random()*1000000000)::int4";
return null;
case "NextDouble":
if (callExp.Object?.Type == typeof(Random)) return "random()";
return null;
case "Random":
if (callExp.Method.DeclaringType.IsNumberType()) return "random()";
return null;
case "ToString":
if (callExp.Object != null)
return callExp.Arguments.Count == 0 ? $"({getExp(callExp.Object)})::text" : null;
return null;
}
var objExp = callExp.Object;
var objType = objExp?.Type;
if (objType?.FullName == "System.Byte[]") return null;
var argIndex = 0;
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable))
{
objExp = callExp.Arguments.FirstOrDefault();
objType = objExp?.Type;
argIndex++;
if (objType == typeof(string))
{
switch (callExp.Method.Name)
{
case "First":
case "FirstOrDefault":
return $"substr({getExp(callExp.Arguments[0])}, 1, 1)";
}
}
}
if (objType == null) objType = callExp.Method.DeclaringType;
if (objType != null || objType.IsArrayOrList())
{
string left = null;
switch (objType.FullName)
{
case "Newtonsoft.Json.Linq.JToken":
case "Newtonsoft.Json.Linq.JObject":
case "Newtonsoft.Json.Linq.JArray":
left = objExp == null ? null : getExp(objExp);
switch (callExp.Method.Name)
{
case "get_Item": return $"{left}->{getExp(callExp.Arguments[argIndex])}";
case "Any": return $"(jsonb_array_length(coalesce({left},'[]')) > 0)";
case "Contains":
var json = getExp(callExp.Arguments[argIndex]);
if (objType == typeof(JArray))
return $"(coalesce({left},'[]') ? ({json})::text)";
if (json.StartsWith("'") && json.EndsWith("'"))
return
$"(coalesce({left},'{{}}') @> {_common.FormatSql("{0}", JToken.Parse(json.Trim('\'')))})";
return $"(coalesce({left},'{{}}') @> ({json})::jsonb)";
case "ContainsKey":
return $"(coalesce({left},'{{}}') ? {getExp(callExp.Arguments[argIndex])})";
case "Concat":
var right2 = getExp(callExp.Arguments[argIndex]);
return $"(coalesce({left},'{{}}') || {right2})";
case "LongCount":
case "Count": return $"jsonb_array_length(coalesce({left},'[]'))";
case "Parse":
var json2 = getExp(callExp.Arguments[argIndex]);
if (json2.StartsWith("'") && json2.EndsWith("'"))
return _common.FormatSql("{0}", JToken.Parse(json2.Trim('\'')));
return $"({json2})::jsonb";
}
break;
}
if (objType == typeof(Dictionary<string, string>))
{
left = objExp == null ? null : getExp(objExp);
switch (callExp.Method.Name)
{
case "get_Item": return $"{left}->{getExp(callExp.Arguments[argIndex])}";
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":
left = objExp == null ? null : getExp(objExp);
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":
tsc.SetMapColumnTmp(null);
var args1 = getExp(callExp.Arguments[argIndex]);
var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess
? tsc.SetDbParamsReturnOld(null)
: null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
tsc.isNotSetMapColumnTmp = true;
left = objExp == null ? null : getExp(objExp);
tsc.isNotSetMapColumnTmp = false;
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
//判断 in 或 array @> array
if (left.StartsWith("array[") || left.EndsWith("]"))
return $"({args1}) in ({left.Substring(6, left.Length - 7)})";
if (left.StartsWith("(") ||
left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定500元素分割, 3空格\r\n4空格
return
$"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
if (args1.StartsWith("(") || args1.EndsWith(")"))
args1 = $"array[{args1.TrimStart('(').TrimEnd(')')}]";
args1 = $"array[{args1}]";
if (objExp != null)
{
var dbinfo = _common._orm.CodeFirst.GetDbInfo(objExp.Type);
if (dbinfo != null) args1 = $"{args1}::{dbinfo.dbtype}";
}
return $"({left} @> {args1})";
case "Concat":
left = objExp == null ? null : getExp(objExp);
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":
left = objExp == null ? null : getExp(objExp);
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 == typeof(Dictionary<string, string>))
{
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 _common.Now;
case "UtcNow": return _common.NowUtc;
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);
case "Format":
if (exp.Arguments[0].NodeType != ExpressionType.Constant)
throw new Exception(
CoreStrings.Not_Implemented_Expression_ParameterUseConstant(exp, exp.Arguments[0]));
var expArgsHack =
exp.Arguments.Count == 2 && exp.Arguments[1].NodeType == ExpressionType.NewArrayInit
? (exp.Arguments[1] as NewArrayExpression).Expressions
: exp.Arguments.Where((a, z) => z > 0);
//3个 {} 时Arguments 解析出来是分开的
//4个 {} 时Arguments[1] 只能解析这个出来,然后里面是 NewArray []
var expArgs = expArgsHack.Select(a =>
{
var atype = (a as UnaryExpression)?.Operand.Type.NullableTypeOrThis() ??
a.Type.NullableTypeOrThis();
if (atype == typeof(string))
return $"'||{_common.IsNull(ExpressionLambdaToSql(a, tsc), "''")}||'";
return $"'||{_common.IsNull($"({ExpressionLambdaToSql(a, tsc)})::text", "''")}||'";
}).ToArray();
return string.Format(ExpressionLambdaToSql(exp.Arguments[0], tsc), expArgs);
case "Join":
if (exp.IsStringJoin(out var tolistObjectExp, out var toListMethod, out var toListArgs1))
{
var newToListArgs0 = Expression.Call(tolistObjectExp, toListMethod,
Expression.Lambda(
Expression.Call(
typeof(SqlExtExtensions).GetMethod("StringJoinPgsqlGroupConcat"),
Expression.Convert(toListArgs1.Body, typeof(object)),
Expression.Convert(exp.Arguments[0], typeof(object))),
toListArgs1.Parameters));
var newToListSql = getExp(newToListArgs0);
return newToListSql;
}
break;
}
}
else
{
var left = getExp(exp.Object);
switch (exp.Method.Name)
{
case "StartsWith":
case "EndsWith":
case "Contains":
var args0Value = getExp(exp.Arguments[0]);
if (args0Value == "NULL") return $"({left}) IS NULL";
if (args0Value.Contains("%"))
{
if (exp.Method.Name == "StartsWith") return $"strpos({args0Value}, {left}) = 1";
if (exp.Method.Name == "EndsWith")
return $"strpos({args0Value}, {left}) = char_length({args0Value})";
return $"strpos({args0Value}, {left}) > 0";
}
var likeOpt = "LIKE";
if (exp.Arguments.Count > 1)
{
if (exp.Arguments[1].Type == typeof(bool) ||
exp.Arguments[1].Type == typeof(StringComparison)) likeOpt = "ILIKE";
}
if (exp.Method.Name == "StartsWith")
return
$"({left}) {likeOpt} {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(({args0Value})::text || '%')")}";
if (exp.Method.Name == "EndsWith")
return
$"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::text)")}";
if (args0Value.StartsWith("'") && args0Value.EndsWith("'"))
return $"({left}) {likeOpt} {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
return $"({left}) {likeOpt} ('%' || ({args0Value})::text || '%')";
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])})::text)";
}
}
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 ExpressionConstDateTime(exp.Arguments[0]) ?? $"({getExp(exp.Arguments[0])})::timestamp";
case "ParseExact":
case "TryParse":
case "TryParseExact":
return ExpressionConstDateTime(exp.Arguments[0]) ?? $"({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})/1000)||' milliseconds')::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.GetGenericArguments().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})/1000)||' milliseconds')::interval)";
}
break;
case "Equals": return $"({left} = ({args1})::timestamp)";
case "CompareTo": return $"extract(epoch from ({left})::timestamp-({args1})::timestamp)";
case "ToString":
if (left.EndsWith("::timestamp") == false) left = $"({left})::timestamp";
if (exp.Arguments.Count == 0) return $"to_char({left},'YYYY-MM-DD HH24:MI:SS.US')";
switch (args1)
{
case "'yyyy-MM-dd HH:mm:ss'": return $"to_char({left},'YYYY-MM-DD HH24:MI:SS')";
case "'yyyy-MM-dd HH:mm'": return $"to_char({left},'YYYY-MM-DD HH24:MI')";
case "'yyyy-MM-dd HH'": return $"to_char({left},'YYYY-MM-DD HH24')";
case "'yyyy-MM-dd'": return $"to_char({left},'YYYY-MM-DD')";
case "'yyyy-MM'": return $"to_char({left},'YYYY-MM')";
case "'yyyyMMddHHmmss'": return $"to_char({left},'YYYYMMDDHH24MISS')";
case "'yyyyMMddHHmm'": return $"to_char({left},'YYYYMMDDHH24MI')";
case "'yyyyMMddHH'": return $"to_char({left},'YYYYMMDDHH24')";
case "'yyyyMMdd'": return $"to_char({left},'YYYYMMDD')";
case "'yyyyMM'": return $"to_char({left},'YYYYMM')";
case "'yyyy'": return $"to_char({left},'YYYY')";
case "'HH:mm:ss'": return $"to_char({left},'HH24:MI:SS')";
}
args1 = Regex.Replace(args1, "(yyyy|yy|MM|dd|HH|hh|mm|ss|tt)", m =>
{
switch (m.Groups[1].Value)
{
case "yyyy": return $"YYYY";
case "yy": return $"YY";
case "MM": return $"%_a1";
case "dd": return $"%_a2";
case "HH": return $"%_a3";
case "hh": return $"%_a4";
case "mm": return $"%_a5";
case "ss": return $"SS";
case "tt": return $"%_a6";
}
return m.Groups[0].Value;
});
var argsFinds = new[] { "YYYY", "YY", "%_a1", "%_a2", "%_a3", "%_a4", "%_a5", "SS", "%_a6" };
var argsSpts = Regex.Split(args1, "(M|d|H|h|m|s|t)");
for (var a = 0; a < argsSpts.Length; a++)
{
switch (argsSpts[a])
{
case "M":
argsSpts[a] = $"ltrim(to_char({left},'MM'),'0')";
break;
case "d":
argsSpts[a] =
$"case when substr(to_char({left},'DD'),1,1) = '0' then substr(to_char({left},'DD'),2,1) else to_char({left},'DD') end";
break;
case "H":
argsSpts[a] =
$"case when substr(to_char({left},'HH24'),1,1) = '0' then substr(to_char({left},'HH24'),2,1) else to_char({left},'HH24') end";
break;
case "h":
argsSpts[a] =
$"case when substr(to_char({left},'HH12'),1,1) = '0' then substr(to_char({left},'HH12'),2,1) else to_char({left},'HH12') end";
break;
case "m":
argsSpts[a] =
$"case when substr(to_char({left},'MI'),1,1) = '0' then substr(to_char({left},'MI'),2,1) else to_char({left},'MI') end";
break;
case "s":
argsSpts[a] =
$"case when substr(to_char({left},'SS'),1,1) = '0' then substr(to_char({left},'SS'),2,1) else to_char({left},'SS') end";
break;
case "t":
argsSpts[a] = $"rtrim(to_char({left},'AM'),'M')";
break;
default:
var argsSptsA = argsSpts[a];
if (argsSptsA.StartsWith("'")) argsSptsA = argsSptsA.Substring(1);
if (argsSptsA.EndsWith("'")) argsSptsA = argsSptsA.Remove(argsSptsA.Length - 1);
argsSpts[a] = argsFinds.Any(m => argsSptsA.Contains(m))
? $"to_char({left},'{argsSptsA}')"
: $"'{argsSptsA}'";
break;
}
}
if (argsSpts.Length > 0) args1 = $"({string.Join(" || ", argsSpts.Where(a => a != "''"))})";
return args1.Replace("%_a1", "MM").Replace("%_a2", "DD").Replace("%_a3", "HH24")
.Replace("%_a4", "HH12").Replace("%_a5", "MI").Replace("%_a6", "AM");
}
}
return null;
}
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc)
{
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
if (exp.Object == null)
{
switch (exp.Method.Name)
{
case "Compare": return $"({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 ExpressionConstDateTime(exp.Arguments[0]) ?? $"({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])})::text";
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;
}
}
class QuestDbExpressionVisitor : ExpressionVisitor
{
private List<string> list = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
list.Add(node.Member.Name);
return node;
}
internal string Fields()
{
var fileds = string.Join(",", list);
list.Clear();
return fileds;
}
}
}

View File

@ -0,0 +1,253 @@
using FreeSql;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
using FreeSql.QuestDb;
using FreeSql.QuestDb.Curd;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public static partial class QuestDbGlobalExtensions
{
/// <summary>
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
/// </summary>
/// <param name="that"></param>
/// <param name="args"></param>
/// <returns></returns>
public static string FormatPostgreSQL(this string that, params object[] args) => _postgresqlAdo.Addslashes(that, args);
static QuestDbAdo _postgresqlAdo = new QuestDbAdo();
/// <summary>
/// PostgreSQL9.5+ 特有的功能On Conflict Do Update<para></para>
/// 注意:此功能会开启插入【自增列】
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <param name="that"></param>
/// <param name="columns">默认是以主键作为重复判断也可以指定其他列a => a.Name | a => new{a.Name,a.Time} | a => new[]{"name","time"}</param>
/// <returns></returns>
public static OnConflictDoUpdate<T1> OnConflictDoUpdate<T1>(this IInsert<T1> that, Expression<Func<T1, object>> columns = null) where T1 : class => new OnConflictDoUpdate<T1>(that.InsertIdentity(), columns);
#region ExecutePgCopy
/// <summary>
/// 批量更新(更新字段数量超过 2000 时收益大)<para></para>
/// 实现原理:使用 PgCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="that"></param>
/// <returns></returns>
public static int ExecutePgCopy<T>(this IUpdate<T> that) where T : class
{
var update = that as UpdateProvider<T>;
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0;
var state = ExecutePgCopyState(update);
return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecutePgCopy());
}
static NativeTuple<string, string, string, string, string[]> ExecutePgCopyState<T>(UpdateProvider<T> update) where T : class
{
if (update._source.Any() != true) return null;
var _table = update._table;
var _commonUtils = update._commonUtils;
var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName;
var tempTableName = $"Temp_{Guid.NewGuid().ToString("N")}";
if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null)
update.WithTransaction(update._orm.Ado.TransactionCurrentThread);
var sb = new StringBuilder().Append("CREATE TEMP TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( ");
var setColumns = new List<string>();
var pkColumns = new List<string>();
foreach (var col in _table.Columns.Values)
{
if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name);
else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name);
else continue;
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" ").Append(col.Attribute.DbType.Replace("NOT NULL", ""));
sb.Append(",");
}
var sql1 = sb.Remove(sb.Length - 1, 1).Append("\r\n) WITH (OIDS=FALSE);").ToString();
sb.Clear().Append("UPDATE ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ")
.Append("\r\nSET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")))
.Append("\r\nFROM ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" b ")
.Append("\r\nWHERE ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")));
var sql2 = sb.ToString();
sb.Clear();
var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}";
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray());
}
/// <summary>
/// PostgreSQL COPY 批量导入功能,封装了 NpgsqlConnection.BeginBinaryImport 方法<para></para>
/// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列<para></para>
/// 使用 WithConnection/WithTransaction 传入连接/事务对象<para></para>
/// 提示:若本方法不能满足,请使用 IInsert&lt;T&gt;.ToDataTable 方法得到 DataTable 对象后,自行处理。<para></para>
/// COPY 与 insert into t values(..),(..),(..) 性能测试参考:<para></para>
/// 插入180000行52列10,090ms 与 46,756ms10列4,081ms 与 9,786ms<para></para>
/// 插入10000行52列583ms 与 3,294ms10列167ms 与 568ms<para></para>
/// 插入5000行52列337ms 与 2,269ms10列93ms 与 366ms<para></para>
/// 插入2000行52列136ms 与 1,019ms10列39ms 与 157ms<para></para>
/// 插入1000行52列88ms 与 374ms10列21ms 与 102ms<para></para>
/// 插入500行52列61ms 与 209ms10列12ms 与 34ms<para></para>
/// 插入100行52列30ms 与 51ms10列4ms 与 9ms<para></para>
/// 插入50行52列25ms 与 37ms10列2ms 与 6ms<para></para>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="that"></param>
public static void ExecutePgCopy<T>(this IInsert<T> that) where T : class
{
var insert = that as QuestDbInsert<T>;
if (insert == null) throw new Exception(CoreStrings.S_Features_Unique("ExecutePgCopy", "PostgreSQL"));
var dt = that.ToDataTable();
if (dt.Rows.Count == 0) return;
Action<NpgsqlConnection> binaryImport = conn =>
{
var copyFromCommand = new StringBuilder().Append("COPY ").Append(insert.InternalCommonUtils.QuoteSqlName(dt.TableName)).Append("(");
var colIndex = 0;
foreach (DataColumn col in dt.Columns)
{
if (colIndex++ > 0) copyFromCommand.Append(", ");
copyFromCommand.Append(insert.InternalCommonUtils.QuoteSqlName(col.ColumnName));
}
copyFromCommand.Append(") FROM STDIN BINARY");
using (var writer = conn.BeginBinaryImport(copyFromCommand.ToString()))
{
foreach (DataRow item in dt.Rows)
writer.WriteRow(item.ItemArray);
writer.Complete();
}
copyFromCommand.Clear();
};
try
{
if (insert.InternalConnection == null && insert.InternalTransaction == null)
{
using (var conn = insert.InternalOrm.Ado.MasterPool.Get())
{
binaryImport(conn.Value as NpgsqlConnection);
}
}
else if (insert.InternalTransaction != null)
{
binaryImport(insert.InternalTransaction.Connection as NpgsqlConnection);
}
else if (insert.InternalConnection != null)
{
var conn = insert.InternalConnection as NpgsqlConnection;
var isNotOpen = false;
if (conn.State != System.Data.ConnectionState.Open)
{
isNotOpen = true;
conn.Open();
}
try
{
binaryImport(conn);
}
finally
{
if (isNotOpen)
conn.Close();
}
}
else
{
throw new NotImplementedException($"ExecutePgCopy {CoreStrings.S_Not_Implemented_FeedBack}");
}
}
finally
{
dt.Clear();
}
}
#if net45
#else
public static Task<int> ExecutePgCopyAsync<T>(this IUpdate<T> that, CancellationToken cancellationToken = default) where T : class
{
var update = that as UpdateProvider<T>;
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0);
var state = ExecutePgCopyState(update);
return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecutePgCopyAsync(cancellationToken));
}
async public static Task ExecutePgCopyAsync<T>(this IInsert<T> that, CancellationToken cancellationToken = default) where T : class
{
var insert = that as QuestDbInsert<T>;
if (insert == null) throw new Exception(CoreStrings.S_Features_Unique("ExecutePgCopyAsync", "PostgreSQL"));
var dt = that.ToDataTable();
if (dt.Rows.Count == 0) return;
Func<NpgsqlConnection, Task> binaryImportAsync = async conn =>
{
var copyFromCommand = new StringBuilder().Append("COPY ").Append(insert.InternalCommonUtils.QuoteSqlName(dt.TableName)).Append("(");
var colIndex = 0;
foreach (DataColumn col in dt.Columns)
{
if (colIndex++ > 0) copyFromCommand.Append(", ");
copyFromCommand.Append(insert.InternalCommonUtils.QuoteSqlName(col.ColumnName));
}
copyFromCommand.Append(") FROM STDIN BINARY");
using (var writer = conn.BeginBinaryImport(copyFromCommand.ToString()))
{
foreach (DataRow item in dt.Rows)
await writer.WriteRowAsync(cancellationToken, item.ItemArray);
writer.Complete();
}
copyFromCommand.Clear();
};
try
{
if (insert.InternalConnection == null && insert.InternalTransaction == null)
{
using (var conn = await insert.InternalOrm.Ado.MasterPool.GetAsync())
{
await binaryImportAsync(conn.Value as NpgsqlConnection);
}
}
else if (insert.InternalTransaction != null)
{
await binaryImportAsync(insert.InternalTransaction.Connection as NpgsqlConnection);
}
else if (insert.InternalConnection != null)
{
var conn = insert.InternalConnection as NpgsqlConnection;
var isNotOpen = false;
if (conn.State != System.Data.ConnectionState.Open)
{
isNotOpen = true;
await conn.OpenAsync(cancellationToken);
}
try
{
await binaryImportAsync(conn);
}
finally
{
if (isNotOpen)
await conn.CloseAsync();
}
}
else
{
throw new NotImplementedException($"ExecutePgCopyAsync {CoreStrings.S_Not_Implemented_FeedBack}");
}
}
finally
{
dt.Clear();
}
}
#endif
#endregion
}

View File

@ -0,0 +1,185 @@
using FreeSql.Internal;
using FreeSql.Internal.CommonProvider;
using FreeSql.PostgreSQL.Curd;
using FreeSql.QuestDb.Curd;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Npgsql.LegacyPostgis;
using NpgsqlTypes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq.Expressions;
using System.Net;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Reflection;
using System.Threading;
namespace FreeSql.QuestDb
{
public class QuestDbProvider<TMark> : BaseDbProvider, IFreeSql<TMark>
{
static QuestDbProvider()
{
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BigInteger)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BitArray)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPoint)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlLine)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlLSeg)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlBox)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPath)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPolygon)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlCircle)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof((IPAddress Address, int Subnet))] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(IPAddress)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PhysicalAddress)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<int>)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<long>)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<decimal>)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<DateTime>)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisPoint)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisLineString)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisPolygon)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiPoint)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiLineString)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiPolygon)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisGeometry)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisGeometryCollection)] = true;
#if nts
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.Point)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.LineString)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.Polygon)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.MultiPoint)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.MultiLineString)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.MultiPolygon)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.Geometry)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NetTopologySuite.Geometries.GeometryCollection)] = true;
#endif
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(Dictionary<string, string>)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JToken)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JObject)] = true;
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JArray)] = true;
var MethodJTokenFromObject = typeof(JToken).GetMethod("FromObject", new[] { typeof(object) });
var MethodJObjectFromObject = typeof(JObject).GetMethod("FromObject", new[] { typeof(object) });
var MethodJArrayFromObject = typeof(JArray).GetMethod("FromObject", new[] { typeof(object) });
var MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) });
var MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) });
var MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) });
var MethodJsonConvertDeserializeObject =
typeof(JsonConvert).GetMethod("DeserializeObject", new[] { typeof(string), typeof(Type) });
var MethodToString = typeof(Utils).GetMethod("ToStringConcat", BindingFlags.Public | BindingFlags.Static,
null, new[] { typeof(object) }, null);
Utils.GetDataReaderValueBlockExpressionSwitchTypeFullName.Add(
(LabelTarget returnTarget, Expression valueExp, Type type) =>
{
switch (type.FullName)
{
case "Newtonsoft.Json.Linq.JToken":
return Expression.IfThenElse(
Expression.TypeIs(valueExp, typeof(string)),
Expression.Return(returnTarget,
Expression.TypeAs(
Expression.Call(MethodJTokenParse,
Expression.Convert(valueExp, typeof(string))), typeof(JToken))),
Expression.Return(returnTarget,
Expression.TypeAs(Expression.Call(MethodJTokenFromObject, valueExp),
typeof(JToken))));
case "Newtonsoft.Json.Linq.JObject":
return Expression.IfThenElse(
Expression.TypeIs(valueExp, typeof(string)),
Expression.Return(returnTarget,
Expression.TypeAs(
Expression.Call(MethodJObjectParse,
Expression.Convert(valueExp, typeof(string))), typeof(JObject))),
Expression.Return(returnTarget,
Expression.TypeAs(Expression.Call(MethodJObjectFromObject, valueExp),
typeof(JObject))));
case "Newtonsoft.Json.Linq.JArray":
return Expression.IfThenElse(
Expression.TypeIs(valueExp, typeof(string)),
Expression.Return(returnTarget,
Expression.TypeAs(
Expression.Call(MethodJArrayParse,
Expression.Convert(valueExp, typeof(string))), typeof(JArray))),
Expression.Return(returnTarget,
Expression.TypeAs(Expression.Call(MethodJArrayFromObject, valueExp),
typeof(JArray))));
case "Npgsql.LegacyPostgis.PostgisGeometry":
return Expression.Return(returnTarget, valueExp);
case "NetTopologySuite.Geometries.Geometry":
return Expression.Return(returnTarget, valueExp);
}
if (typeof(IList).IsAssignableFrom(type))
return Expression.IfThenElse(
Expression.TypeIs(valueExp, typeof(string)),
Expression.Return(returnTarget,
Expression.TypeAs(
Expression.Call(MethodJsonConvertDeserializeObject,
Expression.Convert(valueExp, typeof(string)),
Expression.Constant(type, typeof(Type))), type)),
Expression.Return(returnTarget,
Expression.TypeAs(
Expression.Call(MethodJsonConvertDeserializeObject,
Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(string)),
Expression.Constant(type, typeof(Type))), type)));
return null;
});
Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] =
typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) });
}
public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) =>
new QuestDbSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
public override IInsert<T1> CreateInsertProvider<T1>() =>
new QuestDbInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere) =>
new QuestDbUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere) =>
new QuestDbDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>() =>
new QuestDbInsertOrUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
public QuestDbProvider(string masterConnectionString, string[] slaveConnectionString,
Func<DbConnection> connectionFactory = null)
{
this.InternalCommonUtils = new QuestDbUtils(this);
this.InternalCommonExpression = new QuestDbExpression(this.InternalCommonUtils);
this.Ado = new QuestDbAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString,
connectionFactory);
this.Aop = new AopProvider();
this.DbFirst = new QuestDbDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
this.CodeFirst = new QuestDbCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
//this.Aop.AuditDataReader += (_, e) =>
//{
// var dbtype = e.DataReader.GetDataTypeName(e.Index);
// var m = Regex.Match(dbtype, @"numeric\((\d+)\)", RegexOptions.IgnoreCase);
// if (m.Success && int.Parse(m.Groups[1].Value) > 19)
// e.Value = e.DataReader.GetFieldValue<BigInteger>(e.Index);
//};
}
~QuestDbProvider() => this.Dispose();
int _disposeCounter;
public override void Dispose()
{
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
(this.Ado as AdoProvider)?.Dispose();
}
}
}

View File

@ -0,0 +1,522 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json.Linq;
using Npgsql;
using Npgsql.LegacyPostgis;
using NpgsqlTypes;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Numerics;
using System.Text;
using System.Threading;
using FreeSql.QuestDb;
namespace FreeSql.QuestDb
{
class QuestDbUtils : CommonUtils
{
public QuestDbUtils(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(JToken).FullName, a => string.Concat(a) },
{ typeof(JToken[]).FullName, a => getParamterArrayValue(typeof(string), a, null) },
{ typeof(JObject).FullName, a => string.Concat(a) },
{ typeof(JObject[]).FullName, a => getParamterArrayValue(typeof(string), a, null) },
{ typeof(JArray).FullName, a => string.Concat(a) },
{ typeof(JArray[]).FullName, a => getParamterArrayValue(typeof(string), a, null) },
{ 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) },
{ typeof(char).FullName, a => string.Concat(a).Replace('\0', ' ').ToCharArray().FirstOrDefault() },
{
typeof(BigInteger).FullName,
a => BigInteger.Parse(string.Concat(a), System.Globalization.NumberStyles.Any)
},
{ typeof(BigInteger[]).FullName, a => getParamterArrayValue(typeof(BigInteger), a, 0) },
{ typeof(BigInteger?[]).FullName, a => getParamterArrayValue(typeof(BigInteger?), a, null) },
{
typeof(NpgsqlPath).FullName, a =>
{
var path = (NpgsqlPath)a;
try
{
int count = path.Count;
return path;
}
catch
{
return new NpgsqlPath(new NpgsqlPoint(0, 0));
}
}
},
{
typeof(NpgsqlPath[]).FullName,
a => getParamterArrayValue(typeof(NpgsqlPath), a, new NpgsqlPath(new NpgsqlPoint(0, 0)))
},
{ typeof(NpgsqlPath?[]).FullName, a => getParamterArrayValue(typeof(NpgsqlPath?), a, null) },
{
typeof(NpgsqlPolygon).FullName, a =>
{
var polygon = (NpgsqlPolygon)a;
try
{
int count = polygon.Count;
return polygon;
}
catch
{
return new NpgsqlPolygon(new NpgsqlPoint(0, 0), new NpgsqlPoint(0, 0));
}
}
},
{
typeof(NpgsqlPolygon[]).FullName,
a => getParamterArrayValue(typeof(NpgsqlPolygon), a,
new NpgsqlPolygon(new NpgsqlPoint(0, 0), new NpgsqlPoint(0, 0)))
},
{ typeof(NpgsqlPolygon?[]).FullName, a => getParamterArrayValue(typeof(NpgsqlPolygon?), a, null) },
{
typeof((IPAddress Address, int Subnet)).FullName, a =>
{
var inet = ((IPAddress Address, int Subnet))a;
if (inet.Address == null) return (IPAddress.Any, inet.Subnet);
return inet;
}
},
{
typeof((IPAddress Address, int Subnet)[]).FullName,
a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)), a, (IPAddress.Any, 0))
},
{
typeof((IPAddress Address, int Subnet)?[]).FullName,
a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)?), a, null)
},
};
static object getParamterValue(Type type, object value, int level = 0)
{
if (type.FullName == "System.Byte[]") return value;
if (type.FullName == "System.Char[]") 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 : enumType.CreateInstanceGetDefaultValue())
: getParamterArrayValue(typeof(int), value,
elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue());
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, ColumnInfo col,
Type type, object value)
{
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
if (value != null) value = getParamterValue(type, value);
var ret = new NpgsqlParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
if (value != null && (value.GetType().IsEnum ||
value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true))
{
ret.DataTypeName = "";
}
else
{
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
if (tp != null)
{
var ntp = (NpgsqlDbType)tp;
switch (ntp)
{
case NpgsqlDbType.Boolean:
ret.NpgsqlDbType = NpgsqlDbType.Text;
ret.DbType = DbType.String;
ret.Value = string.Concat(value);
break;
case NpgsqlDbType.Numeric:
ret.NpgsqlDbType = NpgsqlDbType.Double;
ret.DbType = DbType.Double;
ret.Value = Convert.ToDouble(FormatSql("{0}", value));
break;
default:
ret.NpgsqlDbType = (NpgsqlDbType)tp.Value;
break;
}
}
if (col != null)
{
var dbtype = (NpgsqlDbType)_orm.DbFirst.GetDbType(new DatabaseModel.DbColumnInfo
{ DbTypeText = col.DbTypeText });
if (dbtype != NpgsqlDbType.Unknown)
{
ret.NpgsqlDbType = dbtype;
//if (col.DbSize != 0) ret.Size = col.DbSize;
if (col.DbPrecision != 0)
ret.Precision = col.DbPrecision;
if (col.DbScale != 0)
ret.Scale = col.DbScale;
}
}
}
_params?.Add(ret);
return ret;
}
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
Utils.GetDbParamtersByObject<NpgsqlParameter>(sql, obj, "@", (name, type, value) =>
{
if (value != null) value = getParamterValue(type, value);
var ret = new NpgsqlParameter { 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.NpgsqlDbType = (NpgsqlDbType)tp.Value;
//}
return ret;
});
public override string FormatSql(string sql, params object[] args) => sql?.FormatPostgreSQL(args);
public override string QuoteSqlNameAdapter(params string[] name)
{
if (name.Length == 1)
{
var nametrim = name[0].Trim();
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
return nametrim; //原生SQL
if (nametrim.StartsWith("\"") && nametrim.EndsWith("\""))
return nametrim;
return $"\"{nametrim.Replace(".", "\".\"")}\"";
}
return $"\"{string.Join("\".\"", name)}\"";
}
public override string TrimQuoteSqlName(string name)
{
var nametrim = name.Trim();
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
return nametrim; //原生SQL
return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}";
}
public override string[] SplitTableName(string name) => GetSplitTableNames(name, '"', '"', 2);
public override string QuoteParamterName(string name) => $"@{name}";
public override string IsNull(string sql, object value) => $"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 Now => "current_timestamp";
public override string NowUtc => "(current_timestamp at time zone 'UTC')";
public override string QuoteWriteParamterAdapter(Type type, string paramterName) => paramterName;
protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName) => columnName;
static ConcurrentDictionary<Type, bool> _dicIsAssignableFromPostgisGeometry =
new ConcurrentDictionary<Type, bool>();
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, string specialParamFlag,
ColumnInfo col, Type type, object value)
{
if (value == null) return "NULL";
if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value);
if (_dicIsAssignableFromPostgisGeometry.GetOrAdd(type, t2 =>
{
var t2type = t2.IsArray ? t2.GetElementType() : t2;
return typeof(PostgisGeometry).IsAssignableFrom(t2type)
#if nts
||
typeof(NetTopologySuite.Geometries.Geometry).IsAssignableFrom(t2type)
#endif
;
}))
{
var pam = AppendParamter(specialParams, $"p_{specialParams?.Count}{specialParamFlag}", null, type,
value);
return pam.ParameterName;
}
value = getParamterValue(type, value);
var type2 = value.GetType();
if (type2 == typeof(byte[])) return $"'\\x{CommonUtils.BytesSqlRaw(value as byte[])}'";
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, specialParamFlag, col, eleType, item));
}
sb.Append("]");
var dbinfo = _orm.CodeFirst.GetDbInfo(type);
if (dbinfo != null) sb.Append("::").Append(dbinfo.dbtype);
return sb.ToString();
}
else if (type2 == typeof(BitArray))
{
return $"'{(value as BitArray).To1010()}'";
}
else if (type2 == typeof(NpgsqlLine) || type2 == typeof(NpgsqlLine?))
{
var line = value.ToString();
return line == "{0,0,0}" ? "'{0,-1,-1}'" : $"'{line}'";
}
else if (type2 == typeof((IPAddress Address, int Subnet)) ||
type2 == typeof((IPAddress Address, int Subnet)?))
{
var cidr = ((IPAddress Address, int Subnet))value;
return $"'{cidr.Address}/{cidr.Subnet}'";
}
else if (dicGetParamterValue.ContainsKey(type2.FullName))
{
value = string.Concat(value);
}
return FormatSql("{0}", value, 1);
}
}
}
namespace FreeSql
{
public enum SampleUnits
{
/// <summary>
/// 微秒
/// </summary>
U,
/// <summary>
/// 毫秒
/// </summary>
T,
/// <summary>
/// 秒
/// </summary>
s,
/// <summary>
/// 分钟
/// </summary>
m,
/// <summary>
/// 时
/// </summary>
h,
/// <summary>
/// 天
/// </summary>
d,
/// <summary>
/// 月
/// </summary>
M
}
public static class SampleByExtension
{
//是否使用该方法
internal static AsyncLocal<bool> IsExistence = new AsyncLocal<bool>()
{
Value = false
};
internal static AsyncLocal<string> SamoleByString = new AsyncLocal<string>()
{
Value = string.Empty
};
public static void Initialize()
{
IsExistence.Value = false;
SamoleByString.Value = string.Empty;
}
/// <summary>
/// SAMPLE BY用于时间序列数据将大型数据集汇总为同质时间块的聚合作为SELECT语句的一部分。对缺少数据的数据集执行查询的用户可以使用FILL关键字指定填充行为
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="select"></param>
/// <param name="time">时长</param>
/// <param name="unit">单位</param>
/// <returns></returns>
public static ISelect<T> SampleBy<T>(this ISelect<T> select, double time, SampleUnits unit)
{
var _unit = Enum.GetName(typeof(SampleUnits), unit);
IsExistence.Value = true;
var samoleByTemple = $"{Environment.NewLine}SAMPLE BY {{0}}{{1}}{Environment.NewLine}";
SamoleByString.Value = string.Format(samoleByTemple, time.ToString(), _unit);
return select;
}
}
public static class LatestOnExtension
{
//是否使用该方法
internal static AsyncLocal<bool> IsExistence = new AsyncLocal<bool>()
{
Value = false
};
internal static AsyncLocal<string> LatestOnString = new AsyncLocal<string>()
{
Value = string.Empty
};
public static void Initialize()
{
IsExistence.Value = false;
LatestOnString.Value = string.Empty;
}
/// <summary>
/// 对于多个时间序列存储在同一个表中的场景,根据时间戳检索给定键或键组合的最新项。
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="select"></param>
/// <param name="timestamp">时间标识</param>
/// <param name="partition">最新项的列</param>
/// <returns></returns>
public static ISelect<T1> LatestOn<T1, TKey>(this ISelect<T1> select, Expression<Func<T1, DateTime?>> timestamp,
Expression<Func<T1, TKey>> partition)
{
Provider(timestamp, partition);
return select;
}
private static void Provider<T1, TKey>(Expression<Func<T1, DateTime?>> timestamp,
Expression<Func<T1, TKey>> partition)
{
IsExistence.Value = true;
var latestOnTemple = $"{Environment.NewLine}LATEST ON {{0}} PARTITION BY {{1}} ";
var expressionVisitor = new QuestDbExpressionVisitor();
expressionVisitor.Visit(timestamp);
var _timestamp = expressionVisitor.Fields();
expressionVisitor.Visit(partition);
var _partition = expressionVisitor.Fields();
LatestOnString.Value = string.Format(latestOnTemple, _timestamp, _partition);
}
/// <summary>
/// 对于多个时间序列存储在同一个表中的场景,根据时间戳检索给定键或键组合的最新项。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="select"></param>
/// <param name="timestamp">时间标识</param>
/// <param name="partition">最新项的列</param>
/// <returns></returns>
public static ISelect<T1, T2> LatestOn<T1, T2, TKey>(this ISelect<T1, T2> select,
Expression<Func<T1, DateTime?>> timestamp,
Expression<Func<T1, TKey>> partition) where T2 : class
{
Provider(timestamp, partition);
return select;
}
/// <summary>
/// 对于多个时间序列存储在同一个表中的场景,根据时间戳检索给定键或键组合的最新项。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="select"></param>
/// <param name="timestamp">时间标识</param>
/// <param name="partition">最新项的列</param>
/// <returns></returns>
public static ISelect<T1, T2, T3> LatestOn<T1, T2, T3, TKey>(this ISelect<T1, T2, T3> select,
Expression<Func<T1, DateTime?>> timestamp,
Expression<Func<T1, TKey>> partition) where T2 : class where T3 : class
{
Provider(timestamp, partition);
return select;
}
/// <summary>
/// 对于多个时间序列存储在同一个表中的场景,根据时间戳检索给定键或键组合的最新项。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="select"></param>
/// <param name="timestamp">时间标识</param>
/// <param name="partition">最新项的列</param>
/// <returns></returns>
public static ISelect<T1, T2, T3, T4> LatestOn<T1, T2, T3, T4, TKey>(this ISelect<T1, T2, T3, T4> select,
Expression<Func<T1, DateTime?>> timestamp,
Expression<Func<T1, TKey>> partition) where T2 : class where T3 : class where T4 : class
{
Provider(timestamp, partition);
return select;
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FreeSql.Provider.QuestDb.Subtable
{
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class AutoSubtableAttribute : Attribute
{
public SubtableType? SubtableType
{
get; set;
}
public AutoSubtableAttribute(SubtableType type)
{
SubtableType = type;
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FreeSql.Provider.QuestDb.Subtable
{
public enum SubtableType
{
Year = 1,
Month = 2,
Day = 3,
Hour = 4,
Second = 5,
Minute = 6,
Millisecond = 7,
Weekday = 8,
Quarter = 9
}
}

Binary file not shown.