initial commit

This commit is contained in:
tk
2024-11-13 18:18:28 +08:00
commit 013f35e296
1500 changed files with 443723 additions and 0 deletions

View File

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

View File

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

View File

@ -0,0 +1,288 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json;
using System;
using System.Collections;
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();
internal string InternalTableRuleInvoke() => TableRuleInvoke();
private int InternelExecuteAffrows()
{
//如果设置了RestAPI的Url则走HTTP
var sql = ToSql();
var execAsync = RestAPIExtension.ExecAsync(sql).GetAwaiter().GetResult();
var resultHash = new Hashtable();
try
{
resultHash = JsonConvert.DeserializeObject<Hashtable>(execAsync);
}
catch
{
if (execAsync.Contains("401"))
{
throw new Exception("请确认new FreeSqlBuilder().UseQuestDbRestAPI()中设置的用户名密码是否正确.");
}
}
var ddl = resultHash["ddl"]?.ToString();
return ddl?.ToLower() == "ok" ? 1 : 0;
}
public override int ExecuteAffrows()
{
if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl))
{
return base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000,
_batchParameterLimit > 0 ? _batchParameterLimit : 3000);
}
return InternelExecuteAffrows();
}
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)
{
if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl))
{
return base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000,
_batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
}
return Task.FromResult(InternelExecuteAffrows());
}
public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) =>
base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000,
_batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) =>
base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000,
_batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
long ret = 0;
Exception exception = null;
Aop.CurdBeforeEventArgs before = null;
var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
if (identCols.Any() == false)
{
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
try
{
ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql,
_commandTimeout, _params, cancellationToken);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return 0;
}
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
try
{
long.TryParse(
string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql,
_commandTimeout, _params, cancellationToken)), out ret);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return ret;
}
async protected override Task<List<T1>> RawExecuteInsertedAsync(CancellationToken cancellationToken = default)
{
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return new List<T1>();
var sb = new StringBuilder();
sb.Append(sql).Append(" RETURNING ");
var colidx = 0;
foreach (var col in _table.Columns.Values)
{
if (colidx > 0) sb.Append(", ");
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ")
.Append(_commonUtils.QuoteSqlName(col.CsName));
++colidx;
}
sql = sb.ToString();
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
var ret = new List<T1>();
Exception exception = null;
try
{
ret = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction,
CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
}
catch (Exception ex)
{
exception = ex;
throw ex;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
return ret;
}
#endif
}
}

View File

@ -0,0 +1,91 @@
using FreeSql.Internal;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
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 => _updateSetDict.ContainsKey(a.Attribute.Name) ||
_tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && a.Attribute.IsIdentity == false && _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 (_updateSetDict.Any())
{
var findregex = new Regex("(t1|t2)." + _commonUtils.QuoteSqlName("test").Replace("test", "(\\w+)"));
var tableName = _commonUtils.QuoteSqlName(TableRuleInvoke());
foreach (var usd in _updateSetDict)
{
var field = _commonUtils.QuoteSqlName(usd.Key);
var findsql = $"{field} = EXCLUDED.{field}";
var usdval = findregex.Replace(usd.Value, m =>
{
if (m.Groups[1].Value == "t1") return $"{tableName}.{_commonUtils.QuoteSqlName(m.Groups[2].Value)}";
return $"EXCLUDED.{_commonUtils.QuoteSqlName(m.Groups[2].Value)}";
});
sql = sql.Replace(findsql, $"{field} = {usdval}");
}
}
}
if (string.IsNullOrEmpty(sql)) return null;
if (insert._params?.Any() == true) dbParams.AddRange(insert._params);
return sql;
}
}
}
}

View File

@ -0,0 +1,644 @@
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.Where(a => a.Before == false), true);
tb.CascadeBefore = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter.Where(a => a.Before == true), 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) &&
string.IsNullOrEmpty(tbsfrom[b].CascadeBefore)) sb.Append(" ON 1 = 1");
else sb.Append(" ON ").Append(string.Join(" AND ", new[]
{
tbsfrom[b].CascadeBefore,
tbsfrom[b].NavigateCondition ?? tbsfrom[b].On,
tbsfrom[b].Cascade
}.Where(sql => string.IsNullOrEmpty(sql) == false)));
}
break;
}
else
{
if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].CascadeBefore)) sbnav.Append(" AND ").Append(tbsfrom[a].CascadeBefore);
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(string.Join(" AND ", new[]
{
tb.CascadeBefore,
tb.On ?? tb.NavigateCondition,
tb.Cascade
}.Where(sql => string.IsNullOrEmpty(sql) == false)));
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition))sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
}
if (_join.Length > 0) sb.Append(_join);
if (!string.IsNullOrEmpty(_tables[0].CascadeBefore)) sbnav.Append(" AND ").Append(_tables[0].CascadeBefore);
sbnav.Append(_where);
if (!string.IsNullOrEmpty(_tables[0].Cascade)) sbnav.Append(" AND ").Append(_tables[0].Cascade);
if (sbnav.Length > 0)
{
sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
}
if (string.IsNullOrEmpty(_groupby) == false)
{
//sb.Append(_groupby);
if (string.IsNullOrEmpty(_having) == false)
{
var message = @"提示由于QuedtDb语法不支持Having请使用以下语法代替
fsql.Select<Model>()
//.GroupBy(a => a.Id)
//.Having(a => a.Count() > 1)
.WithTempQuery(a => new
{
a.Id,
count1 = SqlExt.Count(a.Id).ToValue()
})
.Where(a => a.count1 > 1)
.ToList();";
throw new NotImplementedException(message);
}
}
sb.Append(_orderby);
//SamoleBy扩展
if (SampleByExtension.IsExistence.Value)
{
sb.Append(SampleByExtension.SamoleByString.Value);
SampleByExtension.Initialize();
}
//LatestOn扩展
if (LatestOnExtension.IsExistence.Value)
{
sb.Append(LatestOnExtension.LatestOnString.Value);
LatestOnExtension.Initialize();
}
if (_limit > 0)
sb.Append(" \r\nlimit ").Append(_skip == 0 ? _limit : _skip);
if (_skip > 0)
//sb.Append(" \r\noffset ").Append(_skip);
sb.Append(",").Append(_skip + _limit);
sbnav.Clear();
if (tbUnionsGt0) sb.Append(") ftb");
}
return sb.Append(_tosqlAppendContent).ToString();
}
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) :
base(orm, commonUtils, commonExpression, dywhere)
{
}
public override ISelect<T1, T2> From<T2>(
Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3> From<T2, T3>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression,
null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>>
exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils,
_commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm,
_commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> From<T2, T3, T4, T5,
T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm,
_commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> From<T2, T3, T4,
T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(
Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
, ISelectFromExpression<T1>>> exp)
{
this.InternalFrom(exp);
var ret = new QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm,
_commonUtils, _commonExpression, null);
QuestDbSelect<T1>.CopyData(this, ret, exp?.Parameters);
return ret;
}
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select,
_distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having,
_orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent,
_whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3>
where T2 : class where T3 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4>
where T2 : class where T3 : class where T4 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5>
where T2 : class where T3 : class where T4 : class where T5 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5,
T6> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4,
T5, T6, T7> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3,
T4, T5, T6, T7, T8> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2,
T3, T4, T5, T6, T7, T8, T9> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : FreeSql.Internal.CommonProvider.
Select11Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : FreeSql.Internal.CommonProvider.
Select12Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : FreeSql.Internal.CommonProvider.
Select13Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : FreeSql.Internal.
CommonProvider.Select14Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
where T14 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> : FreeSql.Internal.
CommonProvider.Select15Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
where T14 : class
where T15 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
class QuestDbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : FreeSql.Internal.
CommonProvider.Select16Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class
where T13 : class
where T14 : class
where T15 : class
where T16 : class
{
public QuestDbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression,
object dywhere) : base(orm, commonUtils, commonExpression, dywhere)
{
}
public override string ToSql(string field = null) => QuestDbSelect<T1>.ToSqlStatic(_commonUtils,
_commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where,
_groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule,
_tosqlAppendContent, _whereGlobalFilter, _orm);
}
}

View File

@ -0,0 +1,244 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json;
using System;
using System.Collections;
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);
private int InternelExecuteAffrows()
{
var sql = ToSql();
var execAsync = RestAPIExtension.ExecAsync(sql).GetAwaiter().GetResult();
var resultHash = new Hashtable();
try
{
resultHash = JsonConvert.DeserializeObject<Hashtable>(execAsync);
}
catch
{
if (execAsync.Contains("401"))
{
throw new Exception("请确认new FreeSqlBuilder().UseQuestDbRestAPI()中设置的用户名密码是否正确.");
}
}
var ddl = resultHash["ddl"]?.ToString();
var updated = Convert.ToInt32(resultHash["updated"]);
return ddl?.ToLower() == "ok" ? updated : 0;
}
public override int ExecuteAffrows()
{
//如果设置了RestAPI中Url则走HTTP
if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl))
{
return base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500,
_batchParameterLimit > 0 ? _batchParameterLimit : 3000);
}
return InternelExecuteAffrows();
}
protected override List<TReturn> ExecuteUpdated<TReturn>(IEnumerable<ColumnInfo> columns) => base.SplitExecuteUpdated<TReturn>(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, columns);
protected override List<TReturn> RawExecuteUpdated<TReturn>(IEnumerable<ColumnInfo> columns)
{
var ret = new List<TReturn>();
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 columns)
{
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 queryType = typeof(TReturn) == typeof(T1) ? (_table.TypeLazy ?? _table.Type) : null;
var rettmp = _orm.Ado.Query<TReturn>(queryType, _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)
{
if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl))
{
return base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500,
_batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
}
return Task.FromResult(InternelExecuteAffrows());
}
protected override Task<List<TReturn>> ExecuteUpdatedAsync<TReturn>(IEnumerable<ColumnInfo> columns, CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync<TReturn>(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, columns, cancellationToken);
async protected override Task<List<TReturn>> RawExecuteUpdatedAsync<TReturn>(IEnumerable<ColumnInfo> columns, CancellationToken cancellationToken = default)
{
var ret = new List<TReturn>();
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 columns)
{
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 queryType = typeof(TReturn) == typeof(T1) ? (_table.TypeLazy ?? _table.Type) : null;
var rettmp = await _orm.Ado.QueryAsync<TReturn>(queryType, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken);
ValidateVersionAndThrow(rettmp.Count, sql, dbParms);
ret.AddRange(rettmp);
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
}
});
sbret?.Clear();
return ret;
}
#endif
}
}

View File

@ -0,0 +1,41 @@
<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.833</Version>
<PackageReadmeFile>readme.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="../../readme.md" Pack="true" PackagePath="\"/>
<None Include="../../logo.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.32" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="CsvHelper" Version="30.0.1" />
<PackageReference Include="Npgsql" Version="5.0.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,121 @@
using FreeSql.Internal;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
using FreeSql.Internal.ObjectPool;
using Newtonsoft.Json.Linq;
using Npgsql;
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.QuestDb, null, null) { }
public QuestDbAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.QuestDb, masterConnectionString, slaveConnectionStrings)
{
base._util = util;
if (connectionFactory != null)
{
var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.QuestDb, connectionFactory);
ConnectionString = pool.TestConnection?.ConnectionString;
MasterPool = pool;
return;
}
var isAdoPool = masterConnectionString?.StartsWith("AdoConnectionPool,") ?? false;
if (isAdoPool) masterConnectionString = masterConnectionString.Substring("AdoConnectionPool,".Length);
if (!string.IsNullOrEmpty(masterConnectionString))
MasterPool = isAdoPool ?
new DbConnectionStringPool(base.DataType, CoreStrings.S_MasterDatabase, () => new NpgsqlConnection(masterConnectionString)) as IObjectPool<DbConnection> :
new QuestDbConnectionPool(CoreStrings.S_MasterDatabase, masterConnectionString, null, null);
slaveConnectionStrings?.ToList().ForEach(slaveConnectionString =>
{
var slavePool = isAdoPool ?
new DbConnectionStringPool(base.DataType, $"{CoreStrings.S_SlaveDatabase}{SlavePools.Count + 1}", () => new NpgsqlConnection(slaveConnectionString)) as IObjectPool<DbConnection> :
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 ? "true" : "false";
else if (param is string)
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
else if (param is Guid || param is Guid?)
return string.Concat("'", ((Guid)param).ToString("n"), "'");
else if (param is char)
return string.Concat("'", param.ToString().Replace("'", "''").Replace('\0', ' '), "'");
else if (param is Enum)
return AddslashesTypeHandler(param.GetType(), param) ?? ((Enum)param).ToInt64();
else if (decimal.TryParse(string.Concat(param), out var trydec))
return param;
else if (param is DateTime)
return AddslashesTypeHandler(typeof(DateTime), param) ?? string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
else if (param is DateTime?)
return AddslashesTypeHandler(typeof(DateTime?), param) ?? string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
else if (param is TimeSpan || param is TimeSpan?)
return (long)((TimeSpan)param).TotalSeconds;
else if (param is byte[])
return $"'\\x{CommonUtils.BytesSqlRaw(param as byte[])}'";
else if (param is JToken || param is JObject || param is JArray)
return string.Concat("'", param.ToString().Replace("'", "''"), "'::jsonb");
else if ((isdic = param is Dictionary<string, string>) ||
param is IEnumerable<KeyValuePair<string, string>>)
{
var pgdics = isdic ? param as Dictionary<string, string> :
param as IEnumerable<KeyValuePair<string, string>>;
var pghstore = new StringBuilder("'");
var pairs = pgdics.ToArray();
for (var i = 0; i < pairs.Length; i++)
{
if (i != 0) pghstore.Append(",");
pghstore.AppendFormat("\"{0}\"=>", pairs[i].Key.Replace("'", "''"));
if (pairs[i].Value == null)
pghstore.Append("NULL");
else
pghstore.AppendFormat("\"{0}\"", pairs[i].Value.Replace("'", "''"));
}
return pghstore.Append("'::hstore");
}
else if (param is IEnumerable)
return AddslashesIEnumerable(param, mapType, mapColumn);
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
}
public override DbCommand CreateCommand()
{
return new NpgsqlCommand();
}
public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
{
var rawPool = pool as QuestDbConnectionPool;
if (rawPool != null) rawPool.Return(conn, ex);
else pool.Return(conn);
}
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
}
}

View File

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

View File

@ -0,0 +1,254 @@
using FreeSql.DataAnnotations;
using FreeSql.Internal;
using FreeSql.Internal.Model;
using FreeSql.Provider.QuestDb.Subtable;
using Newtonsoft.Json.Linq;
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(Guid).FullName, CsToDb.New(NpgsqlDbType.Char, "symbol", "symbol", false, null, Guid.Empty) },
{ typeof(Guid?).FullName, CsToDb.New(NpgsqlDbType.Char, "symbol", "symbol", 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, "binary", "binary", false, null, new byte[0]) },
{ typeof(BigInteger).FullName, CsToDb.New(NpgsqlDbType.Numeric, "long256", "long256 NOT NULL", false, false, 0) },
{ typeof(BigInteger?).FullName, CsToDb.New(NpgsqlDbType.Numeric, "long256", "long256", 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 TypeSchemaAndName[] 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 = obj.tableSchema;
if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.tableSchema.Type.FullName));
if (tb.Columns.Any() == false)
throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.tableSchema.Type.FullName));
var tbname = tb.DbName;
var tboldname = tb.DbOldName;
if (string.IsNullOrEmpty(obj.tableName) == false)
{
var tbtmpname = obj.tableName;
if (tbname != tbtmpname)
{
tbname = tbtmpname;
tboldname = null;
}
}
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.tableSchema.Type.GetProperties())
{
var timeAttr = propety.GetCustomAttribute<AutoSubtableAttribute>();
if (timeAttr != null)
{
var ckey = propety.Name;
//如果存在Column.Name
var colNameAttr = propety.GetCustomAttribute<ColumnAttribute>();
if (!string.IsNullOrWhiteSpace(colNameAttr?.Name))
//则以Column中的Name为主
ckey = colNameAttr.Name;
var colName = tb.Columns.FirstOrDefault(it => it.Key == ckey).Value;
sbalter.Append(
$" TIMESTAMP({colName.Attribute.Name}) PARTITION BY {timeAttr.SubtableType};{Environment.NewLine}");
}
}
}
//如果旧表名特性存在,旧表名在数据库中存在,现表名在数据库中不存在,直接走修改表名逻辑
else if (string.IsNullOrWhiteSpace(tboldname) == false && allTable.Any(s => s.Equals(tboldname)) &&
allTable.Any(s => s.Equals(tbname)) == false)
{
//修改表名
sbalter.Append("RENAME TABLE ")
.Append(_commonUtils.QuoteSqlName(tboldname))
.Append(" TO ").Append(_commonUtils.QuoteSqlName(tbname))
.Append($";{Environment.NewLine}");
}
//如果旧表名特性存在,旧表名在数据库中不存在,现表名在数据库中存在,对比列
//如果旧表名特性不存在 现表名在数据库中存在,对比列
else if ((string.IsNullOrWhiteSpace(tboldname) == false &&
allTable.Any(s => s.Equals(tboldname)) == false &&
allTable.Any(s => s.Equals(tbname)) == true)
|| (string.IsNullOrWhiteSpace(tboldname) == true &&
allTable.Any(s => s.Equals(tbname)) == true))
{
//查询列
var questDbColumnInfo = _orm.Ado.ExecuteArray($"SHOW COLUMNS FROM '{tbname}'").Select(o => new
{
columnName = o[0].ToString(),
indexed = Convert.ToBoolean(o[2])
}).ToList();
//对比列
foreach (var tbcol in tb.ColumnsByPosition)
{
//如果旧列名存在 现列名均不存在 直接添加列
if (questDbColumnInfo.Any(a => a.columnName.Equals(tbcol.Attribute.OldName)) == false
&& questDbColumnInfo.Any(a => a.columnName.Equals(tbcol.Attribute.Name)) ==
false)
{
sbalter.Append("ALTER TABLE ").Append(tbname)
.Append(" ADD COLUMN ").Append(tbcol.Attribute.Name).Append(" ")
.Append(tbcol.Attribute.DbType).Append($";{Environment.NewLine}");
questDbColumnInfo.Add(new
{
columnName = tbcol.Attribute.Name,
indexed = false
});
}
//如果旧列名存在,现列名不存在,直接修改列名
else if (questDbColumnInfo.Any(a =>
a.columnName.ToString().Equals(tbcol.Attribute.OldName)) == true
&& questDbColumnInfo.Any(a => a.columnName.ToString().Equals(tbcol.Attribute.Name)) ==
false)
{
sbalter.Append("ALTER TABLE ").Append(tbname)
.Append(" RENAME COLUMN ").Append(tbcol.Attribute.OldName).Append(" TO ")
.Append(tbcol.Attribute.Name).Append($";{Environment.NewLine}");
}
}
//对比索引
foreach (var uk in tb.Indexes)
{
if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false)
continue;
var ukname = ReplaceIndexName(uk.Name, tbname);
//先判断表中有没此字段的索引
var isIndex = questDbColumnInfo
.Where(a => a.columnName.ToString().Equals(uk.Columns.First().Column.Attribute.Name))
.FirstOrDefault()?.indexed;
//如果此字段不是索引
if (isIndex != null && isIndex == false)
{
//创建索引
sbalter.Append($"ALTER TABLE {tbname} ALTER COLUMN ");
foreach (var tbcol in uk.Columns)
{
if (tbcol.Column.Attribute.DbType != "SYMBOL")
throw new Exception("索引只能是string类型且[Column(DbType = \"symbol\")]");
sbalter.Append($"{tbcol.Column.Attribute.Name}");
}
sbalter.Append($" ADD INDEX;{Environment.NewLine}");
}
}
}
sb.Append(sbalter);
}
return sb.Length == 0 ? null : sb.ToString();
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
namespace FreeSql.Provider.QuestDb
{
internal class QuestDbContainer
{
//作用于HttpClientFatory
private static IServiceCollection Services;
internal static IServiceProvider ServiceProvider { get; private set; }
internal static void Initialize(Action<IServiceCollection> service)
{
Services = new ServiceCollection();
service?.Invoke(Services);
ServiceProvider = Services.BuildServiceProvider();
}
internal static T GetService<T>()
{
return ServiceProvider.GetService<T>();
}
}
}

View File

@ -0,0 +1,793 @@
using FreeSql.DatabaseModel;
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json.Linq;
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")
},
/*** 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")
},
};
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)
{
var tableColumns = _orm.Ado.ExecuteDataTable($"SHOW COLUMNS FROM '{name}'");
List<DbColumnInfo> dbColumnInfos = new List<DbColumnInfo>();
var dbTableInfo = new DbTableInfo()
{
Name = name,
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;
return dbTableInfo;
}
public List<DbTableInfo> GetTablesByDatabase(params string[] database)
{
return GetTables(database, null, false);
}
public List<DbTableInfo> GetTables(string[] database, string tablename, bool ignoreCase)
{
var resList = _orm.Ado.Query<string>(CommandType.Text, @"SHOW TABLES");
var tables = new List<DbTableInfo>();
resList.ForEach(s =>
{
var tableColumns = _orm.Ado.ExecuteDataTable($"SHOW COLUMNS FROM '{s}'");
List<DbColumnInfo> dbColumnInfos = new List<DbColumnInfo>();
var dbTableInfo = new DbTableInfo()
{
Name = s,
Columns = new List<DbColumnInfo>()
};
foreach (DataRow tableColumnsRow in tableColumns.Rows)
{
dbColumnInfos.Add(new DbColumnInfo()
{
Name = tableColumnsRow["column"].ToString(),
DbTypeText = tableColumnsRow["type"].ToString(),
Table = dbTableInfo,
});
}
dbTableInfo.Columns = dbColumnInfos;
tables.Add(dbTableInfo);
});
return tables;
}
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
{
throw new NotImplementedException();
}
public static NativeTuple<bool, int, int> ParsePgVersion(string versionString, int v1, int v2)
{
int[] version = new int[] { 0, 0 };
var vmatch = Regex.Match(versionString, @"(\d+)\.(\d+)");
if (vmatch.Success)
{
version[0] = int.Parse(vmatch.Groups[1].Value);
version[1] = int.Parse(vmatch.Groups[2].Value);
}
else
{
vmatch = Regex.Match(versionString, @"(\d+)");
version[0] = int.Parse(vmatch.Groups[1].Value);
}
if (version[0] > v1)
return NativeTuple.Create(true, version[0], version[1]);
if (version[0] == v1 && version[1] >= v2)
return NativeTuple.Create(true, version[0], version[1]);
return NativeTuple.Create(false, version[0], version[1]);
}
}
}

View File

@ -0,0 +1,634 @@
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;
if (arrOper.Type == typeof(byte[])) return $"length({getExp(arrOper)})";
break;
case ExpressionType.Convert:
var operandExp = (exp as UnaryExpression)?.Operand;
var gentype = exp.Type.NullableTypeOrThis();
if (gentype != operandExp.Type.NullableTypeOrThis())
{
switch (exp.Type.NullableTypeOrThis().ToString())
{
case "System.Boolean":
return $"(cast({getExp(operandExp)} as string) not in ('0','false','f','no'))";
case "System.Byte": return $"cast({getExp(operandExp)} byte)";
case "System.Char": return $"left(cast({getExp(operandExp)} as string), 1)";
case "System.DateTime":
return ExpressionConstDateTime(operandExp) ?? $"cast({getExp(operandExp)} as timestamp)";
case "System.Decimal": return $"cast({getExp(operandExp)} as double)";
case "System.Double": return $"cast({getExp(operandExp)} as double)";
case "System.Int16": return $"cast({getExp(operandExp)} as short)";
case "System.Int32": return $"cast({getExp(operandExp)} as int)";
case "System.Int64": return $"cast({getExp(operandExp)} as long)";
case "System.SByte": return $"cast({getExp(operandExp)} as byte)";
case "System.Single": return $"cast({getExp(operandExp)} as float)";
case "System.String": return $"cast({getExp(operandExp)} as string)";
case "System.UInt16": return $"cast({getExp(operandExp)} as short)";
case "System.UInt32": return $"cast({getExp(operandExp)} as int)";
case "System.UInt64": return $"cast({getExp(operandExp)} long)";
case "System.Guid": return $"cast({getExp(operandExp)} symbol)";
}
}
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 $"(cast({getExp(callExp.Arguments[0])} as string) not in ('0','false','f','no'))";
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} byte)";
case "System.Char": return $"left(cast({getExp(callExp.Arguments[0])} as string), 1)";
case "System.DateTime":
return ExpressionConstDateTime(callExp.Arguments[0]) ?? $"cast({getExp(callExp.Arguments[0])} as timestamp)";
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as double)";
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as double)";
case "System.Int16": return $"cast({getExp(callExp.Arguments[0])} as short)";
case "System.Int32": return $"cast({getExp(callExp.Arguments[0])} as int)";
case "System.Int64": return $"cast({getExp(callExp.Arguments[0])} as long)";
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as byte)";
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as float)";
case "System.String": return $"cast({getExp(callExp.Arguments[0])} as string)";
case "System.UInt16": return $"cast({getExp(callExp.Arguments[0])} as short)";
case "System.UInt32": return $"cast({getExp(callExp.Arguments[0])} as int)";
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} long)";
case "System.Guid": return $"cast({getExp(callExp.Arguments[0])} symbol)";
}
break;
case "NewGuid":
return null;
case "Next":
if (callExp.Object?.Type == typeof(Random)) return "rnd_int()";
return null;
case "NextDouble":
if (callExp.Object?.Type == typeof(Random)) return "rnd_double()";
return null;
case "Random":
if (callExp.Method.DeclaringType.IsNumberType()) return "rnd_int()";
return null;
case "ToString":
if (callExp.Object != null)
{
if (callExp.Object.Type.NullableTypeOrThis().IsEnum)
{
tsc.SetMapColumnTmp(null);
var oldMapType = tsc.SetMapTypeReturnOld(typeof(string));
var enumStr = ExpressionLambdaToSql(callExp.Object, tsc);
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
return enumStr;
}
var value = ExpressionGetValue(callExp.Object, out var success);
if (success) return formatSql(value, typeof(string), null, null);
return callExp.Arguments.Count == 0 ? $"cast({getExp(callExp.Object)} as string)" : 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 $"left({getExp(callExp.Arguments[0])}, 1)";
}
}
}
if (objType == null) objType = callExp.Method.DeclaringType;
if (objType != null || objType.IsArrayOrList())
{
if (argIndex >= callExp.Arguments.Count) break;
tsc.SetMapColumnTmp(null);
var args1 = getExp(callExp.Arguments[argIndex]);
var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
tsc.isNotSetMapColumnTmp = true;
var left = objExp == null ? null : getExp(objExp);
tsc.isNotSetMapColumnTmp = false;
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
switch (callExp.Method.Name)
{
case "Contains":
//判断 in //在各大 Provider AdoProvider 中已约定500元素分割, 3空格\r\n4空格
return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
}
}
break;
case ExpressionType.NewArrayInit:
var arrExp = exp as NewArrayExpression;
var arrSb = new StringBuilder();
arrSb.Append("(");
for (var a = 0; a < arrExp.Expressions.Count; a++)
{
if (a > 0) arrSb.Append(",");
if (a % 500 == 499) arrSb.Append(" \r\n \r\n"); //500元素分割, 3空格\r\n4空格
arrSb.Append(getExp(arrExp.Expressions[a]));
}
if (arrSb.Length == 1) arrSb.Append("NULL");
return arrSb.Append(")").ToString();
case ExpressionType.ListInit:
var listExp = exp as ListInitExpression;
var listSb = new StringBuilder();
listSb.Append("(");
for (var a = 0; a < listExp.Initializers.Count; a++)
{
if (listExp.Initializers[a].Arguments.Any() == false) continue;
if (a > 0) listSb.Append(",");
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
}
if (listSb.Length == 1) listSb.Append("NULL");
return listSb.Append(")").ToString();
case ExpressionType.New:
var newExp = exp as NewExpression;
if (typeof(IList).IsAssignableFrom(newExp.Type))
{
if (newExp.Arguments.Count == 0) return "(NULL)";
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
return getExp(newExp.Arguments[0]);
}
return null;
}
return null;
}
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
{
if (exp.Expression == null)
{
switch (exp.Member.Name)
{
case "Empty": return "''";
}
return null;
}
var left = ExpressionLambdaToSql(exp.Expression, tsc);
switch (exp.Member.Name)
{
case "Length": return $"length({left})";
}
return null;
}
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
{
if (exp.Expression == null)
{
switch (exp.Member.Name)
{
case "Now": return _common.Now;
case "UtcNow": return _common.NowUtc;
case "Today": return $"date_trunc('day',{_common.Now})";
case "MinValue": return "to_timestamp('0001-01-01', 'yyyy-MM-dd')";
case "MaxValue": return "to_timestamp('9999-12-31 23:59:59.999999', 'yyyy-MM-dd HH:mm:ss.SSSUUU')";
}
return null;
}
var left = ExpressionLambdaToSql(exp.Expression, tsc);
switch (exp.Member.Name)
{
case "Date": return $"date_trunc('day',{left})";
case "TimeOfDay": return $"(hour({left})*3600+minute({left})*60+second({left})";
case "DayOfWeek": return $"(day_of_week_sunday_first({left})-1)";
case "Day": return $"day({left})";
case "DayOfYear": return $"datediff('d',date_trunc('year',{left}),{left})";
case "Month": return $"month({left})";
case "Year": return $"year({left})";
case "Hour": return $"hour({left})";
case "Minute": return $"minute({left})";
case "Second": return $"second({left})";
case "Millisecond":return $"millis({left})";
case "Ticks": return $"(extract(epoch from {left})*10000000+millis({left})*10000+micros({left})*10+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})/{60 * 60 * 24})";
case "Hours": return $"floor(({left})/{60 * 60}%24)";
case "Milliseconds": return $"0";
case "Minutes": return $"(floor(({left})/60)%60)";
case "Seconds": return $"(({left})%60)";
case "Ticks": return $"(({left})*{(long)1000000 * 10})";
case "TotalDays": return $"(({left})/{60 * 60 * 24})";
case "TotalHours": return $"(({left})/{60 * 60})";
case "TotalMilliseconds": return $"(({left})*1000)";
case "TotalMinutes": return $"(({left})/60)";
case "TotalSeconds": return $"({left})";
}
return null;
}
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc)
{
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
if (exp.Object == null)
{
switch (exp.Method.Name)
{
case "IsNullOrEmpty":
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 trim({arg2}) = '')";
case "Concat":
if (exp.Arguments.Count == 1 && exp.Arguments[0].NodeType == ExpressionType.NewArrayInit && exp.Arguments[0] is NewArrayExpression concatNewArrExp)
return _common.StringConcat(concatNewArrExp.Expressions.Select(a => getExp(a)).ToArray(), null);
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($"cast({ExpressionLambdaToSql(a, tsc)} as string)", "''")}||'";
}).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({left}, {args0Value}) = 1";
if (exp.Method.Name == "EndsWith") return $"strpos({left}, {args0Value}) = length({left})-length({args0Value})+1";
return $"strpos({left}, {args0Value}) > 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, "%") : $"(cast({args0Value} as string) || '%')")}";
if (exp.Method.Name == "EndsWith")
return $"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || cast({args0Value} as string))")}";
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) {likeOpt} {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
return $"({left}) {likeOpt} ('%' || cast({args0Value} as string) || '%')";
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 $"substring({left}, {substrArgs1})";
return $"substring({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} = cast({getExp(exp.Arguments[0])} as string))";
}
}
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 $"datediff('s',{getExp(exp.Arguments[0])},{getExp(exp.Arguments[1])})";
case "DaysInMonth": return $"days_in_month(to_date({getExp(exp.Arguments[0])} || '-' || {getExp(exp.Arguments[1])},'yyyy-MM'))";
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
case "IsLeapYear": return $"is_leap_year(to_date({getExp(exp.Arguments[0])},'yyyy'))";
case "Parse": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as timestamp)";
case "ParseExact":
case "TryParse":
case "TryParseExact": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as 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 $"dateadd('s',{args1},{left})";
case "AddDays": return $"dateadd('d',{args1},{left})";
case "AddHours": return $"dateadd('h',{args1},{left})";
case "AddMilliseconds": return $"dateadd('s',{args1}/1000,{left})";
case "AddMinutes": return $"dateadd('m',{args1},{left})";
case "AddMonths": return $"dateadd('M',{args1},{left})";
case "AddSeconds": return $"dateadd('s',{args1},{left})";
case "AddTicks": return $"dateadd('s',{args1}/10000000,{left})";
case "AddYears": return $"dateadd('y',{args1},{left})";
case "Subtract":
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
{
case "System.DateTime": return $"datediff('s',{args1},{left})";
case "System.TimeSpan": return $"dateadd('s',({args1})*-1,{left})";
}
break;
case "Equals": return $"({left} = cast({args1} as timestamp))";
case "CompareTo": return $"datediff('s',{args1},{left})";
case "ToString":
if (left.EndsWith(" as timestamp)") == false) left = $"cast({left} as datetime)";
if (exp.Arguments.Count == 0) return $"convert(varchar, {left}, 121)";
switch (args1.TrimStart('N'))
{
case "'yyyy-MM-dd HH:mm:ss'": return $"convert(char(19), {left}, 120)";
case "'yyyy-MM-dd HH:mm'": return $"substring(convert(char(19), {left}, 120), 1, 16)";
case "'yyyy-MM-dd HH'": return $"substring(convert(char(19), {left}, 120), 1, 13)";
case "'yyyy-MM-dd'": return $"convert(char(10), {left}, 23)";
case "'yyyy-MM'": return $"substring(convert(char(10), {left}, 23), 1, 7)";
case "'yyyyMMdd'": return $"convert(char(8), {left}, 112)";
case "'yyyyMM'": return $"substring(convert(char(8), {left}, 112), 1, 6)";
case "'yyyy'": return $"substring(convert(char(8), {left}, 112), 1, 4)";
case "'HH:mm:ss'": return $"convert(char(8), {left}, 24)";
}
var isMatched = false;
var nchar = args1.StartsWith("N'") ? "N" : "";
args1 = Regex.Replace(args1, "(yyyy|yy|MM|M|dd|d|HH|H|hh|h|mm|m|ss|s|tt|t)", m =>
{
isMatched = true;
switch (m.Groups[1].Value)
{
case "yyyy": return $"' + substring(convert(char(8), {left}, 112), 1, 4) + {nchar}'";
case "yy": return $"' + substring(convert(char(6), {left}, 12), 1, 2) + {nchar}'";
case "MM": return $"' + substring(convert(char(6), {left}, 12), 3, 2) + {nchar}'";
case "M": return $"' + case when substring(convert(char(6), {left}, 12), 3, 1) = '0' then substring(convert(char(6), {left}, 12), 4, 1) else substring(convert(char(6), {left}, 12), 3, 2) end + {nchar}'";
case "dd": return $"' + substring(convert(char(6), {left}, 12), 5, 2) + {nchar}'";
case "d": return $"' + case when substring(convert(char(6), {left}, 12), 5, 1) = '0' then substring(convert(char(6), {left}, 12), 6, 1) else substring(convert(char(6), {left}, 12), 5, 2) end + {nchar}'";
case "HH": return $"' + substring(convert(char(8), {left}, 24), 1, 2) + {nchar}'";
case "H": return $"' + case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end + {nchar}'";
case "hh":
return $"' + case cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) % 12" +
$"when 0 then '12' when 1 then '01' when 2 then '02' when 3 then '03' when 4 then '04' when 5 then '05' when 6 then '06' when 7 then '07' when 8 then '08' when 9 then '09' when 10 then '10' when 11 then '11' end + {nchar}'";
case "h":
return $"' + case cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) % 12" +
$"when 0 then '12' when 1 then '1' when 2 then '2' when 3 then '3' when 4 then '4' when 5 then '5' when 6 then '6' when 7 then '7' when 8 then '8' when 9 then '9' when 10 then '10' when 11 then '11' end + {nchar}'";
case "mm": return $"' + substring(convert(char(8), {left}, 24), 4, 2) + {nchar}'";
case "m": return $"' + case when substring(convert(char(8), {left}, 24), 4, 1) = '0' then substring(convert(char(8), {left}, 24), 5, 1) else substring(convert(char(8), {left}, 24), 4, 2) end + {nchar}'";
case "ss": return $"' + substring(convert(char(8), {left}, 24), 7, 2) + {nchar}'";
case "s": return $"' + case when substring(convert(char(8), {left}, 24), 7, 1) = '0' then substring(convert(char(8), {left}, 24), 8, 1) else substring(convert(char(8), {left}, 24), 7, 2) end + {nchar}'";
case "tt": return $"' + case when cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) >= 12 then 'PM' else 'AM' end + {nchar}'";
case "t": return $"' + case when cast(case when substring(convert(char(8), {left}, 24), 1, 1) = '0' then substring(convert(char(8), {left}, 24), 2, 1) else substring(convert(char(8), {left}, 24), 1, 2) end as int) >= 12 then 'P' else 'A' end + {nchar}'";
}
return m.Groups[0].Value;
});
return isMatched == false ? args1 : $"({args1})";
}
}
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])})*{60 * 60 * 24})";
case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})";
case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)";
case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)";
case "FromSeconds": return $"({getExp(exp.Arguments[0])})";
case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)";
case "Parse": return $"cast({getExp(exp.Arguments[0])} as long)";
case "ParseExact":
case "TryParse":
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as long)";
}
}
else
{
var left = getExp(exp.Object);
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
switch (exp.Method.Name)
{
case "Add": return $"({left}+{args1})";
case "Subtract": return $"({left}-({args1}))";
case "Equals": return $"({left} = {args1})";
case "CompareTo": return $"({left}-({args1}))";
case "ToString": return $"cast({left} as string)";
}
}
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 $"(cast({getExp(exp.Arguments[0])} as string) not in ('0','false','f','no'))";
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as byte)";
case "ToChar": return $"left(cast({getExp(exp.Arguments[0])} as string), 1)";
case "ToDateTime":
return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as timestamp)";
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as double)";
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as double)";
case "ToInt16": return $"cast({getExp(exp.Arguments[0])} as short)";
case "ToInt32": return $"cast({getExp(exp.Arguments[0])} as int)";
case "ToInt64": return $"cast({getExp(exp.Arguments[0])} as long)";
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as byte)";
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as int)";
case "ToString": return $"cast({getExp(exp.Arguments[0])} as string)";
case "ToUInt16": return $"cast({getExp(exp.Arguments[0])} as short)";
case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as int)";
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as long)";
}
}
return null;
}
}
class QuestDbExpressionVisitor : ExpressionVisitor
{
private List<string> list = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
list.Add(node.Member.Name);
return node;
}
internal string Fields()
{
var fileds = string.Join(",", list);
list.Clear();
return fileds;
}
}
}

View File

@ -0,0 +1,252 @@
using FreeSql;
using FreeSql.DataAnnotations;
using FreeSql.Internal.CommonProvider;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Text;
using System.Threading;
/// <summary>
/// QuestDB lambda 表达式树扩展解析<para></para>
/// https://questdb.io/docs/reference/function/aggregation/
/// </summary>
[ExpressionCall]
public static class QuestFunc
{
public static ThreadLocal<ExpressionCallContext> expContext = new ThreadLocal<ExpressionCallContext>();
static ExpressionCallContext ec => expContext.Value;
static T call<T>(string rt)
{
ec.Result = rt;
return default(T);
}
public static decimal avg(object value) => call<decimal>($"avg({ec.ParsedContent["value"]})");
public static long count() => call<long>($"count(*)");
public static long count(object column_name) => call<long>($"count({ec.ParsedContent["column_name"]})");
public static long count_distinct(object column_name) => call<long>($"count_distinct({ec.ParsedContent["column_name"]})");
public static string first(object column_name) => call<string>($"first({ec.ParsedContent["column_name"]})");
public static string last(object column_name) => call<string>($"last({ec.ParsedContent["column_name"]})");
public static decimal haversine_dist_deg(decimal lat, decimal lon, DateTime ts) => call<decimal>($"haversine_dist_deg({ec.ParsedContent["lat"]},{ec.ParsedContent["lon"]},{ec.ParsedContent["ts"]})");
public static decimal ksum(object value) => call<decimal>($"ksum({ec.ParsedContent["value"]})");
public static T max<T>(T value) => call<T>($"max({ec.ParsedContent["value"]})");
public static T min<T>(T value) => call<T>($"min({ec.ParsedContent["value"]})");
public static decimal nsum(object value) => call<decimal>($"nsum({ec.ParsedContent["value"]})");
public static decimal stddev_samp(object value) => call<decimal>($"stddev_samp({ec.ParsedContent["value"]})");
public static decimal sum(object value) => call<decimal>($"sum({ec.ParsedContent["value"]})");
public static bool isOrdered(object column) => call<bool>($"isOrdered({ec.ParsedContent["column"]})");
public static T coalesce<T>(object value1, object value2) => call<T>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]})");
public static T coalesce<T>(object value1, object value2, object value3) => call<T>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]})");
public static T coalesce<T>(object value1, object value2, object value3, object value4) => call<T>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]})");
public static T coalesce<T>(object value1, object value2, object value3, object value4, object value5) => call<T>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]})");
public static T nullif<T>(object value1, object value2) => call<T>($"nullif({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]})");
public static DateTime date_trunc([RawValue] date_trunc_unit unit, object timestamp) => call<DateTime>($"date_trunc('{unit.ToString()}',{ec.ParsedContent["timestamp"]})");
public enum date_trunc_unit
{
millennium,
decade,
century,
year,
quarter,
month,
week,
day,
hour,
minute,
second,
milliseconds,
microseconds,
}
public static DateTime dateadd([RawValue] date_period period, [RawValue] long n, object startDate) => call<DateTime>($"dateadd('{(char)(int)period}',{n},{ec.ParsedContent["startDate"]})");
public static long datediff([RawValue] date_period period, object date1, object date2) => call<long>($"datediff('{(char)period}',{ec.ParsedContent["date1"]},{ec.ParsedContent["date2"]})");
public enum date_period
{
second = 's',
minute = 'm',
hour = 'h',
day = 'd',
week = 'w',
month = 'M',
year = 'y',
}
public static int day(object timestamp) => call<int>($"day({ec.ParsedContent["timestamp"]})");
public static int day_of_week(object timestamp) => call<int>($"day_of_week({ec.ParsedContent["timestamp"]})");
public static int day_of_week_sunday_first(object timestamp) => call<int>($"day_of_week_sunday_first({ec.ParsedContent["timestamp"]})");
public static long extract([RawValue] extract_unit unit, object timestamp) => call<int>($"extract({unit.ToString()} from {ec.ParsedContent["timestamp"]})");
public enum extract_unit
{
millennium,
epoch,
decade,
century,
year,
isoyear,
/// <summary>
/// day of year
/// </summary>
doy,
quarter,
month,
week,
/// <summary>
/// day of week
/// </summary>
dow,
isodow,
day,
hour,
minute,
second,
microseconds,
milliseconds,
}
public static int hour(object timestamp) => call<int>($"hour({ec.ParsedContent["timestamp"]})");
public static bool is_leap_year(object timestamp) => call<bool>($"is_leap_year({ec.ParsedContent["timestamp"]})");
public static bool days_in_month(object timestamp) => call<bool>($"days_in_month({ec.ParsedContent["timestamp"]})");
public static int micros(object timestamp) => call<int>($"micros({ec.ParsedContent["timestamp"]})");
public static int millis(object timestamp) => call<int>($"millis({ec.ParsedContent["timestamp"]})");
public static int minute(object timestamp) => call<int>($"minute({ec.ParsedContent["timestamp"]})");
public static int month(object timestamp) => call<int>($"month({ec.ParsedContent["timestamp"]})");
public static DateTime now() => call<DateTime>($"now()");
public static DateTime pg_postmaster_start_time() => call<DateTime>($"pg_postmaster_start_time()");
public static int second(object timestamp) => call<int>($"second({ec.ParsedContent["timestamp"]})");
/// <summary>
/// Use now() with WHERE clause filter.
/// </summary>
/// <returns></returns>
public static DateTime systimestamp() => call<DateTime>($"systimestamp()");
/// <summary>
/// Use now() with WHERE clause filter.
/// </summary>
/// <returns></returns>
public static DateTime sysdate() => call<DateTime>($"sysdate()");
public static DateTime timestamp_ceil([RawValue] timestamp_ceil_unit unit, object timestamp) => call<DateTime>($"timestamp_ceil({(char)unit},{ec.ParsedContent["timestamp"]})");
public static DateTime timestamp_floor([RawValue] timestamp_ceil_unit unit, object timestamp) => call<DateTime>($"timestamp_floor({(char)unit},{ec.ParsedContent["timestamp"]})");
public enum timestamp_ceil_unit
{
milliseconds = 'T',
seconds = 's',
minutes = 'm',
hours = 'h',
days = 'd',
months = 'M',
year = 'y',
}
public static DateTime timestamp_shuffle(object timestamp_1, object timestamp_2) => call<DateTime>($"timestamp_shuffle({ec.ParsedContent["timestamp_1"]},{ec.ParsedContent["timestamp_2"]})");
public static DateTime to_date(string str, string format) => call<DateTime>($"to_date({ec.ParsedContent["str"]},{ec.ParsedContent["format"]})");
public static string to_str(DateTime value, string format) => call<string>($"to_str({ec.ParsedContent["value"]},{ec.ParsedContent["format"]})");
public static DateTime to_timestamp(string str, string format) => call<DateTime>($"to_timestamp({ec.ParsedContent["str"]},{ec.ParsedContent["format"]})");
public static DateTime to_timezone(DateTime timestamp, string timezone) => call<DateTime>($"to_timezone({ec.ParsedContent["timestamp"]},{ec.ParsedContent["timezone"]})");
public static DateTime to_utc(DateTime timestamp, string timezone) => call<DateTime>($"to_utc({ec.ParsedContent["timestamp"]},{ec.ParsedContent["timezone"]})");
public static int week_of_year(object timestamp) => call<int>($"week_of_year({ec.ParsedContent["timestamp"]})");
public static int year(object timestamp) => call<int>($"year({ec.ParsedContent["timestamp"]})");
public static T abs<T>(T value) => call<T>($"abs({ec.ParsedContent["value"]})");
public static decimal log(object value) => call<decimal>($"log({ec.ParsedContent["value"]})");
public static decimal power(object _base, object exponent) => call<decimal>($"power({ec.ParsedContent["_base"]},{ec.ParsedContent["exponent"]})");
public static T round<T>(T value, int scale) => call<T>($"round({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})");
public static T round_down<T>(T value, int scale) => call<T>($"round_down({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})");
public static T round_half_even<T>(T value, int scale) => call<T>($"round_half_even({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})");
public static T round_up<T>(T value, int scale) => call<T>($"round_up({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})");
public static string size_pretty(long value) => call<string>($"size_pretty({ec.ParsedContent["value"]})");
public static decimal sqrt(object value) => call<decimal>($"sqrt({ec.ParsedContent["value"]})");
public static bool rnd_boolean() => call<bool>($"rnd_boolean()");
public static byte rnd_byte() => call<byte>($"rnd_byte()");
public static byte rnd_byte(byte min, byte max) => call<byte>($"rnd_byte({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})");
public static short rnd_short() => call<short>($"rnd_short()");
public static short rnd_short(short min, short max) => call<short>($"rnd_short({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})");
public static int rnd_int() => call<int>($"rnd_int()");
public static int rnd_int(int min, int max) => call<int>($"rnd_int({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})");
public static int rnd_int(int min, int max, int nanRate) => call<int>($"rnd_int({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})");
public static long rnd_long() => call<long>($"rnd_long()");
public static long rnd_long(long min, long max) => call<long>($"rnd_long({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})");
public static long rnd_long(long min, long max, int nanRate) => call<long>($"rnd_long({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})");
public static BigInteger rnd_long256() => call<BigInteger>($"rnd_long256()");
public static BigInteger rnd_long256(BigInteger min, BigInteger max) => call<BigInteger>($"rnd_long256({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})");
public static BigInteger rnd_long256(BigInteger min, BigInteger max, int nanRate) => call<BigInteger>($"rnd_long256({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})");
public static float rnd_float() => call<float>($"rnd_float()");
public static float rnd_float(int nanRate) => call<float>($"rnd_float({ec.ParsedContent["nanRate"]})");
public static double rnd_double() => call<double>($"rnd_double()");
public static double rnd_double(int nanRate) => call<double>($"rnd_double({ec.ParsedContent["nanRate"]})");
public static DateTime rnd_date(DateTime min, DateTime max) => call<DateTime>($"rnd_date({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},0)");
public static DateTime rnd_date(DateTime min, DateTime max, int nanRate) => call<DateTime>($"rnd_date({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})");
public static DateTime rnd_timestamp(DateTime min, DateTime max) => call<DateTime>($"rnd_timestamp({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},0)");
public static DateTime rnd_timestamp(DateTime min, DateTime max, int nanRate) => call<DateTime>($"rnd_timestamp({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})");
public static char rnd_char() => call<char>($"rnd_char()");
public static string rnd_symbol([RawValue] string[] symbolList) => call<string>($"rnd_symbol({string.Join(",", symbolList.Select(a => ec.FormatSql(a)))})");
public static string rnd_symbol(int list_size, int minLength, int maxLength, int nullRate) => call<string>($"rnd_symbol({ec.ParsedContent["list_size"]},{ec.ParsedContent["minLength"]},{ec.ParsedContent["maxLength"]},{ec.ParsedContent["nullRate"]})");
public static string rnd_str([RawValue] string[] stringList) => call<string>($"rnd_str({string.Join(",", stringList.Select(a => ec.FormatSql(a)))})");
public static string rnd_str(int list_size, int minLength, int maxLength, int nullRate) => call<string>($"rnd_str({ec.ParsedContent["list_size"]},{ec.ParsedContent["minLength"]},{ec.ParsedContent["maxLength"]},{ec.ParsedContent["nullRate"]})");
public static byte[] rnd_bin() => call<byte[]>($"rnd_bin()");
public static byte[] rnd_bin(long minBytes, int maxBytes, int nullRate) => call<byte[]>($"rnd_bin({ec.ParsedContent["minBytes"]},{ec.ParsedContent["maxBytes"]},{ec.ParsedContent["nullRate"]})");
public static Guid rnd_uuid4() => call<Guid>($"rnd_uuid4()");
public static byte[] rnd_geohash(int bits) => call<byte[]>($"rnd_geohash({ec.ParsedContent["bits"]})");
public static byte[] make_geohash(decimal lon, decimal lat, int bits) => call<byte[]>($"make_geohash({ec.ParsedContent["lon"]},{ec.ParsedContent["lat"]},{ec.ParsedContent["bits"]})");
public static string concat(object value1, object value2) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]})");
public static string concat(object value1, object value2, object value3) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]})");
public static string concat(object value1, object value2, object value3, object value4) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]})");
public static string concat(object value1, object value2, object value3, object value4, object value5) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]})");
public static string concat(object value1, object value2, object value3, object value4, object value5, object value6) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]})");
public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]})");
public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7, object value8) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]},{ec.ParsedContent["value8"]})");
public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7, object value8, object value9) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]},{ec.ParsedContent["value8"]},{ec.ParsedContent["value9"]})");
public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7, object value8, object value9, object value10) => call<string>($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]},{ec.ParsedContent["value8"]},{ec.ParsedContent["value9"]},{ec.ParsedContent["value10"]})");
public static long length(object value) => call<long>($"length({ec.ParsedContent["value"]})");
public static string left(string str, int count) => call<string>($"left({ec.ParsedContent["str"]},{ec.ParsedContent["count"]})");
public static string right(string str, int count) => call<string>($"right({ec.ParsedContent["str"]},{ec.ParsedContent["count"]})");
public static int strpos(object str, string substr) => call<int>($"strpos({ec.ParsedContent["str"]},{ec.ParsedContent["substr"]})");
public static string substring(string str, int start, int length) => call<string>($"substring({ec.ParsedContent["str"]},{ec.ParsedContent["start"]},{ec.ParsedContent["length"]})");
public static string lower(string str) => call<string>($"lower({ec.ParsedContent["str"]})");
public static string upper(string str) => call<string>($"upper({ec.ParsedContent["str"]})");
public static DateTime timestamp_sequence(DateTime startTimestamp, long step) => call<DateTime>($"timestamp_sequence({ec.ParsedContent["startTimestamp"]},{ec.ParsedContent["step"]})");
public static string regexp_replace(string str1, string regex, string str2) => call<string>($"regexp_replace({ec.ParsedContent["str1"]},{ec.ParsedContent["regex"]},{ec.ParsedContent["str2"]})");
public static bool regex_match(string str, string regex) => call<bool>($"{ec.ParsedContent["str"]} ~ {ec.ParsedContent["regex"]}");
public static bool regex_not_match(string str, string regex) => call<bool>($"{ec.ParsedContent["str"]} !~ {ec.ParsedContent["regex"]}");
public static bool like(string str, string pattern) => call<bool>($"{ec.ParsedContent["str"]} LIKE {ec.ParsedContent["pattern"]}");
public static bool not_like(string str, string pattern) => call<bool>($"{ec.ParsedContent["str"]} NOT LIKE {ec.ParsedContent["pattern"]}");
public static bool ilike(string str, string pattern) => call<bool>($"{ec.ParsedContent["str"]} ILIKE {ec.ParsedContent["pattern"]}");
public static bool not_ilike(string str, string pattern) => call<bool>($"{ec.ParsedContent["str"]} NOT ILIKE {ec.ParsedContent["pattern"]}");
public static bool within(object geo, object geohash1) => call<bool>($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]})");
public static bool within(object geo, object geohash1, object geohash2) => call<bool>($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]})");
public static bool within(object geo, object geohash1, object geohash2, object geohash3) => call<bool>($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]},{ec.ParsedContent["geohash3"]})");
public static bool within(object geo, object geohash1, object geohash2, object geohash3, object geohash4) => call<bool>($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]},{ec.ParsedContent["geohash3"]},{ec.ParsedContent["geohash4"]})");
public static bool within(object geo, object geohash1, object geohash2, object geohash3, object geohash4, object geohash5) => call<bool>($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]},{ec.ParsedContent["geohash3"]},{ec.ParsedContent["geohash4"]},{ec.ParsedContent["geohash5"]})");
}
partial class QuestDbGlobalExtensions
{
/// <summary>
/// QuestDB lambda 表达式树扩展解析<para></para>
/// fsql.SelectLongSequence(10, () => new { str1 = qdbfunc.rnd_str(10, 5, 8, 0), ... })...<para></para>
/// SELECT rnd_str(10,5,8,0) FROM long_sequence(10)
/// </summary>
public static ISelect<T> SelectLongSequence<T>(this IFreeSql fsql, long iterations, Expression<Func<T>> selector)
{
var selector2 = Expression.Lambda<Func<object, T>>(selector.Body, Expression.Parameter(typeof(object), "a"));
var tablename = $"(long_sequence ({iterations}))";
return fsql.Select<object>().AsTable((t, old) => tablename).WithTempQuery(selector2);
}
/// <summary>
/// QuestDB lambda 表达式树扩展解析<para></para>
/// fsql.SelectTimestampSequence(10, () => new { str1 = qdbfunc.rnd_str(10, 5, 8, 0), ... })...<para></para>
/// SELECT rnd_str(10,5,8,0) FROM long_sequence(10)
/// </summary>
public static ISelect<T> SelectTimestampSequence<T>(this IFreeSql fsql, DateTime startTimestamp, TimeSpan step, Expression<Func<T>> selector)
{
var selector2 = Expression.Lambda<Func<object, T>>(selector.Body, Expression.Parameter(typeof(object), "a"));
var tablename = $"(timestamp_sequence (to_timestamp('{startTimestamp.ToString("yyyy-MM-dd HH:mm:ss")}', 'yyyy-MM-dd HH:mm:ss'), {Math.Ceiling(step.TotalMilliseconds / 1000)}))";
return fsql.Select<object>().AsTable((t, old) => tablename).WithTempQuery(selector2);
}
}

View File

@ -0,0 +1,349 @@
using CsvHelper;
using FreeSql;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
using FreeSql.QuestDb;
using FreeSql.QuestDb.Curd;
using Newtonsoft.Json;
using Npgsql;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using FreeSql.Provider.QuestDb;
using System.Net;
public static partial class QuestDbGlobalExtensions
{
/// <summary>
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
/// </summary>
/// <param name="that"></param>
/// <param name="args"></param>
/// <returns></returns>
public static string FormatQuestDb(this string that, params object[] args) =>
_questDbAdo.Addslashes(that, args);
private static readonly QuestDbAdo _questDbAdo = new QuestDbAdo();
public static FreeSqlBuilder UseQuestDbRestAPI(this FreeSqlBuilder build, string host, string username = "",
string password = "") => RestAPIExtension.UseQuestDbRestAPI(build, host, username, password);
/// <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)
{
LatestOnExtension.InternelImpl(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> LatestOn<T1, T2, TKey>(this ISelect<T1, T2> select,
Expression<Func<T1, DateTime?>> timestamp,
Expression<Func<T1, TKey>> partition) where T2 : class
{
LatestOnExtension.InternelImpl(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
{
LatestOnExtension.InternelImpl(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
{
LatestOnExtension.InternelImpl(timestamp, partition);
return select;
}
/// <summary>
/// SAMPLE BY用于时间序列数据将大型数据集汇总为同质时间块的聚合作为SELECT语句的一部分。对缺少数据的数据集执行查询的用户可以使用FILL关键字指定填充行为
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="select"></param>
/// <param name="time">时长</param>
/// <param name="unit">单位</param>
/// <param name="alignToCalendar">对准日历</param>
/// <returns></returns>
public static ISelect<T> SampleBy<T>(this ISelect<T> select, double time, SampleUnit unit,
bool alignToCalendar = false)
{
SampleByExtension.IsExistence.Value = true;
var samoleByTemple = $"{Environment.NewLine}SAMPLE BY {{0}}{{1}} {{2}}";
string alignToCalendarTemple = "";
if (alignToCalendar) alignToCalendarTemple = "ALIGN TO CALENDAR ";
SampleByExtension.SamoleByString.Value =
string.Format(samoleByTemple, time.ToString(), (char)unit, alignToCalendarTemple);
return select;
}
/// <summary>
/// 逐行读取,包含空行
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private static List<string> SplitByLine(string text)
{
List<string> lines = new List<string>();
byte[] array = Encoding.UTF8.GetBytes(text);
using (MemoryStream stream = new MemoryStream(array))
{
using (var sr = new StreamReader(stream))
{
string line = sr.ReadLine();
while (line != null)
{
lines.Add(line);
line = sr.ReadLine();
}
}
}
return lines;
}
/// <summary>
/// 批量快速插入
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="that"></param>
/// <param name="dateFormat">导入时,时间格式 默认:yyyy/M/d H:mm:ss</param>
/// <returns></returns>
public static async Task<int> ExecuteQuestDbBulkCopyAsync<T>(this IInsert<T> that,
string dateFormat = "yyyy/M/d H:mm:ss") where T : class
{
//思路通过提供的RestAPI imp实现快速复制
if (string.IsNullOrWhiteSpace(RestAPIExtension.BaseUrl))
{
throw new Exception(
"BulkCopy功能需要启用RestAPI启用方式new FreeSqlBuilder().UseQuestDbRestAPI(\"localhost:9000\", \"username\", \"password\")");
}
var result = 0;
try
{
var client = QuestDbContainer.GetService<IHttpClientFactory>().CreateClient();
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
var list = new List<Hashtable>();
var insert = that as QuestDbInsert<T>;
var name = insert.InternalTableRuleInvoke(); //获取表名
insert.InternalOrm.DbFirst.GetTableByName(name).Columns.ForEach(d =>
{
if (d.DbTypeText == "TIMESTAMP")
{
list.Add(new Hashtable()
{
{ "name", d.Name },
{ "type", d.DbTypeText },
{ "pattern", dateFormat }
});
}
else
{
list.Add(new Hashtable()
{
{ "name", d.Name },
{ "type", d.DbTypeText }
});
}
});
var schema = JsonConvert.SerializeObject(list);
using (MemoryStream stream = new MemoryStream())
{
//写入CSV文件
using (var writer = new StreamWriter(stream))
using (var csv = new CsvWriter(writer, CultureInfo.CurrentCulture))
{
await csv.WriteRecordsAsync(insert._source);
}
var httpContent = new MultipartFormDataContent(boundary);
if (!string.IsNullOrWhiteSpace(RestAPIExtension.authorization))
client.DefaultRequestHeaders.Add("Authorization", RestAPIExtension.authorization);
httpContent.Add(new StringContent(schema), "schema");
httpContent.Add(new ByteArrayContent(stream.ToArray()), "data");
//boundary带双引号 可能导致服务器错误情况
httpContent.Headers.Remove("Content-Type");
httpContent.Headers.TryAddWithoutValidation("Content-Type",
"multipart/form-data; boundary=" + boundary);
var httpResponseMessage =
await client.PostAsync($"{RestAPIExtension.BaseUrl}/imp?name={name}", httpContent);
var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync();
var splitByLine = SplitByLine(readAsStringAsync);
foreach (var s in splitByLine)
{
if (s.Contains("Rows"))
{
var strings = s.Split('|');
if (strings[1].Trim() == "Rows imported")
{
result = Convert.ToInt32(strings[2].Trim());
}
}
}
}
}
catch (Exception e)
{
throw e;
}
return result;
}
/// <summary>
/// 批量快速插入
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="insert"></param>
/// <param name="dateFormat">导入时,时间格式 默认:yyyy/M/d H:mm:ss</param>
/// <returns></returns>
public static int ExecuteQuestDbBulkCopy<T>(this IInsert<T> insert, string dateFormat = "yyyy/M/d H:mm:ss") where T : class
{
return ExecuteQuestDbBulkCopyAsync(insert, dateFormat).ConfigureAwait(false).GetAwaiter().GetResult();
}
}
static class SampleByExtension
{
//是否使用该方法
internal static AsyncLocal<bool> IsExistence = new AsyncLocal<bool>()
{
Value = false
};
internal static AsyncLocal<string> SamoleByString = new AsyncLocal<string>()
{
Value = string.Empty
};
internal static void Initialize()
{
IsExistence.Value = false;
SamoleByString.Value = string.Empty;
}
}
static class LatestOnExtension
{
//是否使用该方法
internal static AsyncLocal<bool> IsExistence = new AsyncLocal<bool>()
{
Value = false
};
internal static AsyncLocal<string> LatestOnString = new AsyncLocal<string>()
{
Value = string.Empty
};
internal static void Initialize()
{
IsExistence.Value = false;
LatestOnString.Value = string.Empty;
}
internal static void InternelImpl<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);
}
}
static class RestAPIExtension
{
internal static string BaseUrl = string.Empty;
internal static string authorization = string.Empty;
internal static async Task<string> ExecAsync(string sql)
{
//HTTP GET 执行SQL
var result = string.Empty;
var client = QuestDbContainer.GetService<IHttpClientFactory>().CreateClient();
var url = $"{BaseUrl}/exec?query={HttpUtility.UrlEncode(sql)}";
if (!string.IsNullOrWhiteSpace(authorization))
client.DefaultRequestHeaders.Add("Authorization", authorization);
var httpResponseMessage = await client.GetAsync(url);
result = await httpResponseMessage.Content.ReadAsStringAsync();
return result;
}
internal static FreeSqlBuilder UseQuestDbRestAPI(FreeSqlBuilder buider, string host, string username = "",
string password = "")
{
BaseUrl = host;
if (BaseUrl.EndsWith("/"))
BaseUrl = BaseUrl.Remove(BaseUrl.Length - 1);
if (!BaseUrl.ToLower().StartsWith("http"))
BaseUrl = $"http://{BaseUrl}";
//生成TOKEN
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
authorization = $"Basic {base64}";
}
//RestApi需要无参数
buider.UseNoneCommandParameter(true);
return buider;
}
}

View File

@ -0,0 +1,175 @@
using FreeSql.Internal;
using FreeSql.Internal.CommonProvider;
using FreeSql.Provider.QuestDb;
using FreeSql.QuestDb.Curd;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
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;
using Microsoft.Extensions.DependencyInjection;
namespace FreeSql.QuestDb
{
public class QuestDbProvider<TMark> : BaseDbProvider, IFreeSql<TMark>
{
static int _firstInit = 1;
static void InitInternal()
{
if (Interlocked.Exchange(ref _firstInit, 0) == 1) //不能放在 static ctor .NetFramework 可能报初始化类型错误
{
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(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) });
QuestDbContainer.Initialize(service =>
{
service.AddHttpClient();
});
}
}
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)
{
InitInternal();
this.InternalCommonUtils = new QuestDbUtils(this);
this.InternalCommonExpression = new QuestDbExpression(this.InternalCommonUtils);
this.Ado = new QuestDbAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString,
connectionFactory);
this.Aop = new AopProvider();
this.DbFirst = new QuestDbDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
this.CodeFirst = new QuestDbCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
//this.Aop.AuditDataReader += (_, e) =>
//{
// var dbtype = e.DataReader.GetDataTypeName(e.Index);
// var m = Regex.Match(dbtype, @"numeric\((\d+)\)", RegexOptions.IgnoreCase);
// if (m.Success && int.Parse(m.Groups[1].Value) > 19)
// e.Value = e.DataReader.GetFieldValue<BigInteger>(e.Index);
//};
}
~QuestDbProvider() => this.Dispose();
int _disposeCounter;
public override void Dispose()
{
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
(this.Ado as AdoProvider)?.Dispose();
}
}
}

View File

@ -0,0 +1,315 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using Newtonsoft.Json.Linq;
using Npgsql;
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.Net.Http;
using System.Numerics;
using System.Text;
using System.Threading;
using FreeSql.QuestDb;
using System.Web;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.IO;
using CsvHelper;
namespace FreeSql.QuestDb
{
class QuestDbUtils : CommonUtils
{
string now_to_timezone;
public QuestDbUtils(IFreeSql orm) : base(orm)
{
var timeOffset = (int)DateTime.Now.Subtract(DateTime.UtcNow).TotalMinutes;
if (timeOffset == 0) now_to_timezone = "systimestamp()";
else
{
var absTimeOffset = Math.Abs(timeOffset);
now_to_timezone = $"to_timezone(systimestamp(),'{(timeOffset > 0 ? '+' : '-')}{(absTimeOffset / 60).ToString().PadLeft(2, '0')}:{(absTimeOffset % 60).ToString().PadLeft(2, '0')}')";
}
}
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?.FormatQuestDb(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 => now_to_timezone;
public override string NowUtc => "systimestamp()";
public override string QuoteWriteParamterAdapter(Type type, string paramterName) => paramterName;
protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName) => columnName;
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, string specialParamFlag,
ColumnInfo col, Type type, object value)
{
if (value == null) return "NULL";
if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value);
value = getParamterValue(type, value);
var type2 = value.GetType();
if (type2 == typeof(byte[])) return $"'\\x{CommonUtils.BytesSqlRaw(value as byte[])}'";
else if (dicGetParamterValue.ContainsKey(type2.FullName))
{
value = string.Concat(value);
}
return FormatSql("{0}", value, 1);
}
}
}
namespace FreeSql
{
public enum SampleUnit
{
microsecond = 'U',
millisecond = 'T',
second = 's',
minute = 'm',
hour = 'h',
day = 'd',
month = 'M',
year = 'y',
}
}

View File

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

View File

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

Binary file not shown.