mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
Add QuestDb
This commit is contained in:
parent
fbd062ab2d
commit
0ef87ad028
217
Providers/FreeSql.Provider.QuestDb/Curd/OnConflictDoUpdate.cs
Normal file
217
Providers/FreeSql.Provider.QuestDb/Curd/OnConflictDoUpdate.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
32
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbDelete.cs
Normal file
32
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbDelete.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
217
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbInsert.cs
Normal file
217
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbInsert.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
645
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbSelect.cs
Normal file
645
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbSelect.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
187
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbUpdate.cs
Normal file
187
Providers/FreeSql.Provider.QuestDb/Curd/QuestDbUpdate.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
109
Providers/FreeSql.Provider.QuestDb/QuestDbAdo/QuestDbAdo.cs
Normal file
109
Providers/FreeSql.Provider.QuestDb/QuestDbAdo/QuestDbAdo.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
278
Providers/FreeSql.Provider.QuestDb/QuestDbCodeFirst.cs
Normal file
278
Providers/FreeSql.Provider.QuestDb/QuestDbCodeFirst.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
783
Providers/FreeSql.Provider.QuestDb/QuestDbDbFirst.cs
Normal file
783
Providers/FreeSql.Provider.QuestDb/QuestDbDbFirst.cs
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
831
Providers/FreeSql.Provider.QuestDb/QuestDbExpression.cs
Normal file
831
Providers/FreeSql.Provider.QuestDb/QuestDbExpression.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
253
Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs
Normal file
253
Providers/FreeSql.Provider.QuestDb/QuestDbGlobalExtensions.cs
Normal 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<T>.ToDataTable 方法得到 DataTable 对象后,自行处理。<para></para>
|
||||||
|
/// COPY 与 insert into t values(..),(..),(..) 性能测试参考:<para></para>
|
||||||
|
/// 插入180000行,52列:10,090ms 与 46,756ms,10列:4,081ms 与 9,786ms<para></para>
|
||||||
|
/// 插入10000行,52列:583ms 与 3,294ms,10列:167ms 与 568ms<para></para>
|
||||||
|
/// 插入5000行,52列:337ms 与 2,269ms,10列:93ms 与 366ms<para></para>
|
||||||
|
/// 插入2000行,52列:136ms 与 1,019ms,10列:39ms 与 157ms<para></para>
|
||||||
|
/// 插入1000行,52列:88ms 与 374ms,10列:21ms 与 102ms<para></para>
|
||||||
|
/// 插入500行,52列:61ms 与 209ms,10列:12ms 与 34ms<para></para>
|
||||||
|
/// 插入100行,52列:30ms 与 51ms,10列:4ms 与 9ms<para></para>
|
||||||
|
/// 插入50行,52列:25ms 与 37ms,10列: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
|
||||||
|
}
|
185
Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs
Normal file
185
Providers/FreeSql.Provider.QuestDb/QuestDbProvider.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
522
Providers/FreeSql.Provider.QuestDb/QuestDbUtils.cs
Normal file
522
Providers/FreeSql.Provider.QuestDb/QuestDbUtils.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Providers/FreeSql.Provider.QuestDb/Subtable/SubtableType.cs
Normal file
19
Providers/FreeSql.Provider.QuestDb/Subtable/SubtableType.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
BIN
Providers/FreeSql.Provider.QuestDb/key.snk
Normal file
BIN
Providers/FreeSql.Provider.QuestDb/key.snk
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user