mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-19 04:18:16 +08:00
- 增加 DuckDB 数据库支持;
This commit is contained in:
26
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbDelete.cs
Normal file
26
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbDelete.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Duckdb.Curd
|
||||
{
|
||||
|
||||
class DuckdbDelete<T1> : Internal.CommonProvider.DeleteProvider<T1>
|
||||
{
|
||||
public DuckdbDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteDeleted() => throw new NotImplementedException($"FreeSql.Provider.Duckdb {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException($"FreeSql.Provider.Duckdb {CoreStrings.S_Not_Implemented_Feature}");
|
||||
#endif
|
||||
}
|
||||
}
|
217
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbInsert.cs
Normal file
217
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbInsert.cs
Normal file
@ -0,0 +1,217 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Duckdb.Curd
|
||||
{
|
||||
|
||||
class DuckdbInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
|
||||
{
|
||||
public DuckdbInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression)
|
||||
{
|
||||
}
|
||||
|
||||
internal IFreeSql InternalOrm => _orm;
|
||||
internal TableInfo InternalTable => _table;
|
||||
internal DbParameter[] InternalParams => _params;
|
||||
internal DbConnection InternalConnection => _connection;
|
||||
internal DbTransaction InternalTransaction => _transaction;
|
||||
internal CommonUtils InternalCommonUtils => _commonUtils;
|
||||
internal CommonExpression InternalCommonExpression => _commonExpression;
|
||||
internal List<T1> InternalSource => _source;
|
||||
internal Dictionary<string, bool> InternalIgnore => _ignore;
|
||||
internal void InternalClearData() => ClearData();
|
||||
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||
|
||||
protected override long RawExecuteIdentity()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
|
||||
var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
|
||||
if (identCols.Any() == false)
|
||||
{
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
try
|
||||
{
|
||||
ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
try
|
||||
{
|
||||
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params)), out ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override List<T1> RawExecuteInserted()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
ret = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||
public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||
|
||||
async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
|
||||
var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
|
||||
if (identCols.Any() == false)
|
||||
{
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
try
|
||||
{
|
||||
ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
try
|
||||
{
|
||||
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken)), out ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Duckdb.Curd
|
||||
{
|
||||
|
||||
class DuckdbInsertOrUpdate<T1> : Internal.CommonProvider.InsertOrUpdateProvider<T1> where T1 : class
|
||||
{
|
||||
public DuckdbInsertOrUpdate(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
222
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbSelect.cs
Normal file
222
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbSelect.cs
Normal file
@ -0,0 +1,222 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Duckdb.Curd
|
||||
{
|
||||
|
||||
class DuckdbSelect<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)
|
||||
sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
|
||||
}
|
||||
sb.Append(_orderby);
|
||||
if (_limit > 0)
|
||||
sb.Append(" \r\nlimit ").Append(_limit);
|
||||
if (_skip > 0)
|
||||
sb.Append(" \r\noffset ").Append(_skip);
|
||||
|
||||
sbnav.Clear();
|
||||
if (tbUnionsGt0) sb.Append(") ftb");
|
||||
}
|
||||
return sb.Append(_tosqlAppendContent).ToString();
|
||||
}
|
||||
|
||||
public DuckdbSelect(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 DuckdbSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm, _commonUtils, _commonExpression, null); DuckdbSelect<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 DuckdbSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
|
||||
{
|
||||
public DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T2 : class where T3 : class
|
||||
{
|
||||
public DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T2 : class where T3 : class where T4 : class
|
||||
{
|
||||
public DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<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 DuckdbSelect<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 DuckdbSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => DuckdbSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||
}
|
||||
}
|
96
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbUpdate.cs
Normal file
96
Providers/FreeSql.Provider.Duckdb/Curd/DuckdbUpdate.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Duckdb.Curd
|
||||
{
|
||||
|
||||
class DuckdbUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1>
|
||||
{
|
||||
|
||||
public DuckdbUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere)
|
||||
{
|
||||
}
|
||||
|
||||
internal string InternalTableAlias { get; set; }
|
||||
internal StringBuilder InternalSbSet => _set;
|
||||
internal StringBuilder InternalSbSetIncr => _setIncr;
|
||||
internal Dictionary<string, bool> InternalIgnore => _ignore;
|
||||
internal void InternalResetSource(List<T1> source) => _source = source;
|
||||
internal string InternalWhereCaseSource(string CsName, Func<string, string> thenValue) => WhereCaseSource(CsName, thenValue);
|
||||
internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col);
|
||||
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||
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) => throw new NotImplementedException($"FreeSql.Provider.Duckdb {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
|
||||
{
|
||||
if (primarys.Length == 1)
|
||||
{
|
||||
var pk = primarys.First();
|
||||
if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append(".");
|
||||
caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
|
||||
return;
|
||||
}
|
||||
caseWhen.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in primarys)
|
||||
{
|
||||
if (pkidx > 0) caseWhen.Append(" || '+' || ");
|
||||
if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append(".");
|
||||
caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::text");
|
||||
++pkidx;
|
||||
}
|
||||
caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
|
||||
{
|
||||
if (primarys.Length == 1)
|
||||
{
|
||||
sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d)));
|
||||
return;
|
||||
}
|
||||
sb.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in primarys)
|
||||
{
|
||||
if (pkidx > 0) sb.Append(" || '+' || ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", pk.GetDbValue(d))).Append("::text");
|
||||
++pkidx;
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col)
|
||||
{
|
||||
if (_noneParameter == false) return;
|
||||
if (col.Attribute.MapType == typeof(string))
|
||||
{
|
||||
sb.Append("::text");
|
||||
return;
|
||||
}
|
||||
var dbtype = _commonUtils.CodeFirst.GetDbInfo(col.Attribute.MapType)?.dbtype;
|
||||
if (dbtype == null) return;
|
||||
|
||||
sb.Append("::").Append(dbtype);
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||
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);
|
||||
protected override Task<List<TReturn>> RawExecuteUpdatedAsync<TReturn>(IEnumerable<ColumnInfo> columns, CancellationToken cancellationToken = default) => throw new NotImplementedException($"FreeSql.Provider.Duckdb {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
217
Providers/FreeSql.Provider.Duckdb/Curd/OnConflictDoUpdate.cs
Normal file
217
Providers/FreeSql.Provider.Duckdb/Curd/OnConflictDoUpdate.cs
Normal file
@ -0,0 +1,217 @@
|
||||
using FreeSql.Aop;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Duckdb.Curd
|
||||
{
|
||||
public class OnConflictDoUpdate<T1> where T1 : class
|
||||
{
|
||||
internal DuckdbInsert<T1> _duckdbInsert;
|
||||
internal DuckdbUpdate<T1> _duckdbUpdatePriv;
|
||||
internal DuckdbUpdate<T1> _duckdbUpdate => _duckdbUpdatePriv ??
|
||||
(_duckdbUpdatePriv = new DuckdbUpdate<T1>(_duckdbInsert.InternalOrm, _duckdbInsert.InternalCommonUtils, _duckdbInsert.InternalCommonExpression, null) { InternalTableAlias = "EXCLUDED" }
|
||||
.NoneParameter().SetSource(_duckdbInsert.InternalSource) as DuckdbUpdate<T1>);
|
||||
internal ColumnInfo[] _tempPrimarys;
|
||||
bool _doNothing;
|
||||
|
||||
public OnConflictDoUpdate(IInsert<T1> insert, Expression<Func<T1, object>> columns = null)
|
||||
{
|
||||
_duckdbInsert = insert as DuckdbInsert<T1>;
|
||||
if (_duckdbInsert == null) throw new Exception(CoreStrings.S_Features_Unique("OnConflictDoUpdate", "Duckdb"));
|
||||
if (_duckdbInsert._noneParameterFlag == "c") _duckdbInsert._noneParameterFlag = "cu";
|
||||
|
||||
if (columns != null)
|
||||
{
|
||||
var colsList = new List<ColumnInfo>();
|
||||
var cols = _duckdbInsert.InternalCommonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, null, columns?.Body, false, null).ToDictionary(a => a, a => true);
|
||||
foreach (var col in _duckdbInsert.InternalTable.Columns.Values)
|
||||
if (cols.ContainsKey(col.Attribute.Name))
|
||||
colsList.Add(col);
|
||||
_tempPrimarys = colsList.ToArray();
|
||||
}
|
||||
if (_tempPrimarys == null || _tempPrimarys.Any() == false)
|
||||
_tempPrimarys = _duckdbInsert.InternalTable.Primarys;
|
||||
if (_tempPrimarys.Any() == false) throw new Exception(CoreStrings.S_OnConflictDoUpdate_MustIsPrimary);
|
||||
}
|
||||
|
||||
protected void ClearData()
|
||||
{
|
||||
_duckdbInsert.InternalClearData();
|
||||
_duckdbUpdatePriv = null;
|
||||
}
|
||||
|
||||
public OnConflictDoUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns)
|
||||
{
|
||||
_duckdbUpdate.IgnoreColumns(columns);
|
||||
return this;
|
||||
}
|
||||
public OnConflictDoUpdate<T1> UpdateColumns(Expression<Func<T1, object>> columns)
|
||||
{
|
||||
_duckdbUpdate.UpdateColumns(columns);
|
||||
return this;
|
||||
}
|
||||
public OnConflictDoUpdate<T1> IgnoreColumns(string[] columns)
|
||||
{
|
||||
_duckdbUpdate.IgnoreColumns(columns);
|
||||
return this;
|
||||
}
|
||||
public OnConflictDoUpdate<T1> UpdateColumns(string[] columns)
|
||||
{
|
||||
_duckdbUpdate.UpdateColumns(columns);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OnConflictDoUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> column, TMember value)
|
||||
{
|
||||
_duckdbUpdate.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)
|
||||
//{
|
||||
// _duckdbUpdate.Set(exp);
|
||||
// return this;
|
||||
//}
|
||||
public OnConflictDoUpdate<T1> SetRaw(string sql)
|
||||
{
|
||||
_duckdbUpdate.SetRaw(sql);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OnConflictDoUpdate<T1> DoNothing()
|
||||
{
|
||||
_doNothing = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public string ToSql()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_duckdbInsert.ToSql()).Append("\r\nON CONFLICT(");
|
||||
for (var a = 0; a < _tempPrimarys.Length; a++)
|
||||
{
|
||||
if (a > 0) sb.Append(", ");
|
||||
sb.Append(_duckdbInsert.InternalCommonUtils.QuoteSqlName(_tempPrimarys[a].Attribute.Name));
|
||||
}
|
||||
if (_doNothing)
|
||||
{
|
||||
sb.Append(") DO NOTHING");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(") DO UPDATE SET\r\n");
|
||||
|
||||
if (_duckdbUpdate._tempPrimarys.Any() == false) _duckdbUpdate._tempPrimarys = _tempPrimarys;
|
||||
var sbSetEmpty = _duckdbUpdate.InternalSbSet.Length == 0;
|
||||
var sbSetIncrEmpty = _duckdbUpdate.InternalSbSetIncr.Length == 0;
|
||||
if (sbSetEmpty == false || sbSetIncrEmpty == false)
|
||||
{
|
||||
if (sbSetEmpty == false) sb.Append(_duckdbUpdate.InternalSbSet.ToString().Substring(2));
|
||||
if (sbSetIncrEmpty == false) sb.Append(sbSetEmpty ? _duckdbUpdate.InternalSbSetIncr.ToString().Substring(2) : _duckdbUpdate.InternalSbSetIncr.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
var colidx = 0;
|
||||
foreach (var col in _duckdbInsert.InternalTable.Columns.Values)
|
||||
{
|
||||
if (col.Attribute.IsPrimary || _duckdbUpdate.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 = _duckdbInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name);
|
||||
sb.Append(field).Append(" = ").Append(_duckdbInsert.InternalCommonUtils.QuoteSqlName(_duckdbInsert.InternalTable.DbName)).Append(".").Append(field).Append(" + 1");
|
||||
}
|
||||
else if (_duckdbInsert.InternalIgnore.ContainsKey(col.Attribute.Name))
|
||||
{
|
||||
if (string.IsNullOrEmpty(col.DbUpdateValue) == false)
|
||||
{
|
||||
sb.Append(_duckdbInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ").Append(col.DbUpdateValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var caseWhen = _duckdbUpdate.InternalWhereCaseSource(col.CsName, sqlval => sqlval).Trim();
|
||||
sb.Append(caseWhen);
|
||||
if (caseWhen.EndsWith(" END")) _duckdbUpdate.InternalToSqlCaseWhenEnd(sb, col);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var field = _duckdbInsert.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(_duckdbInsert.InternalTable.Type, _duckdbInsert.InternalTable, CurdType.Insert, sql, _duckdbInsert.InternalParams);
|
||||
_duckdbInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_duckdbInsert, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
ret = _duckdbInsert.InternalOrm.Ado.ExecuteNonQuery(_duckdbInsert.InternalConnection, _duckdbInsert.InternalTransaction, CommandType.Text, sql, _duckdbInsert._commandTimeout, _duckdbInsert.InternalParams);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new CurdAfterEventArgs(before, exception, ret);
|
||||
_duckdbInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_duckdbInsert, 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(_duckdbInsert.InternalTable.Type, _duckdbInsert.InternalTable, CurdType.Insert, sql, _duckdbInsert.InternalParams);
|
||||
_duckdbInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_duckdbInsert, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
ret = await _duckdbInsert.InternalOrm.Ado.ExecuteNonQueryAsync(_duckdbInsert.InternalConnection, _duckdbInsert.InternalTransaction, CommandType.Text, sql, _duckdbInsert._commandTimeout, _duckdbInsert.InternalParams, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new CurdAfterEventArgs(before, exception, ret);
|
||||
_duckdbInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_duckdbInsert, after);
|
||||
ClearData();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
116
Providers/FreeSql.Provider.Duckdb/DuckdbAdo/DuckdbAdo.cs
Normal file
116
Providers/FreeSql.Provider.Duckdb/DuckdbAdo/DuckdbAdo.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using FreeSql.Internal.ObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using DuckDB.NET.Data;
|
||||
using static DuckDB.NET.Native.NativeMethods;
|
||||
|
||||
namespace FreeSql.Duckdb
|
||||
{
|
||||
class DuckdbAdo : FreeSql.Internal.CommonProvider.AdoProvider
|
||||
{
|
||||
public DuckdbAdo() : base(DataType.DuckDB, null, null) { }
|
||||
public DuckdbAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.DuckDB, masterConnectionString, slaveConnectionStrings)
|
||||
{
|
||||
base._util = util;
|
||||
if (connectionFactory != null)
|
||||
{
|
||||
var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.DuckDB, connectionFactory);
|
||||
ConnectionString = pool.TestConnection?.ConnectionString;
|
||||
MasterPool = pool;
|
||||
_CreateCommandConnection = pool.TestConnection;
|
||||
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, () => DuckdbConnectionPool.CreateConnection(masterConnectionString)) as IObjectPool<DbConnection> :
|
||||
new DuckdbConnectionPool(CoreStrings.S_MasterDatabase, masterConnectionString, null, null);
|
||||
|
||||
slaveConnectionStrings?.ToList().ForEach(slaveConnectionString =>
|
||||
{
|
||||
var slavePool = isAdoPool ?
|
||||
new DbConnectionStringPool(base.DataType, $"{CoreStrings.S_SlaveDatabase}{SlavePools.Count + 1}", () => DuckdbConnectionPool.CreateConnection(slaveConnectionString)) as IObjectPool<DbConnection> :
|
||||
new DuckdbConnectionPool($"{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 = Utils.GetDataReaderValue(mapType, param);
|
||||
|
||||
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 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("timestamp '", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
else if (param is DateTime?)
|
||||
return AddslashesTypeHandler(typeof(DateTime?), param) ?? string.Concat("timestamp '", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
|
||||
#if net60
|
||||
else if (param is DateOnly || param is DateOnly?)
|
||||
return AddslashesTypeHandler(typeof(DateOnly), param) ?? string.Concat("date '", ((DateOnly)param).ToString("yyyy-MM-dd"), "'");
|
||||
else if (param is TimeOnly || param is TimeOnly?)
|
||||
{
|
||||
var ts = (TimeOnly)param;
|
||||
return $"'{ts.Hour}:{ts.Minute}:{ts.Second}.{ts.Millisecond}'";
|
||||
}
|
||||
#endif
|
||||
|
||||
else if (param is TimeSpan || param is TimeSpan?)
|
||||
{
|
||||
var ts = (TimeSpan)param;
|
||||
return $"time '{Math.Min(24, (int)Math.Floor(ts.TotalHours))}:{ts.Minutes}:{ts.Seconds}.{ts.Milliseconds}'";
|
||||
}
|
||||
else if (param is byte[])
|
||||
{
|
||||
var sb = new StringBuilder().Append("'");
|
||||
foreach (var vc in param as byte[]) sb.Append("\\x").Append(vc.ToString("X2"));
|
||||
return sb.Append("'::blob").ToString();
|
||||
}
|
||||
else if (param is IEnumerable)
|
||||
return AddslashesIEnumerable(param, mapType, mapColumn);
|
||||
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
}
|
||||
|
||||
DbConnection _CreateCommandConnection;
|
||||
public override DbCommand CreateCommand()
|
||||
{
|
||||
if (_CreateCommandConnection != null)
|
||||
{
|
||||
var cmd = _CreateCommandConnection.CreateCommand();
|
||||
cmd.Connection = null;
|
||||
return cmd;
|
||||
}
|
||||
return new DuckDBCommand();
|
||||
}
|
||||
|
||||
public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
|
||||
{
|
||||
var rawPool = pool as DuckdbConnectionPool;
|
||||
if (rawPool != null) rawPool.Return(conn, ex);
|
||||
else pool.Return(conn);
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
using DuckDB.NET.Data;
|
||||
using FreeSql.Internal.ObjectPool;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Duckdb
|
||||
{
|
||||
|
||||
class DuckdbConnectionPool : ObjectPool<DbConnection>
|
||||
{
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
|
||||
public DuckdbConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null)
|
||||
{
|
||||
this.availableHandler = availableHandler;
|
||||
this.unavailableHandler = unavailableHandler;
|
||||
policy = new DuckdbConnectionPoolPolicy
|
||||
{
|
||||
_pool = this,
|
||||
Name = name
|
||||
};
|
||||
this.Policy = policy;
|
||||
policy.ConnectionString = connectionString;
|
||||
}
|
||||
|
||||
public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false)
|
||||
{
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
|
||||
internal DuckdbConnectionPoolPolicy policy;
|
||||
|
||||
public static DbConnection CreateConnection(string connectionString)
|
||||
{
|
||||
var conn = new DuckDBConnection(connectionString);
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
class DuckdbConnectionPoolPolicy : IPolicy<DbConnection>
|
||||
{
|
||||
|
||||
internal DuckdbConnectionPool _pool;
|
||||
public string Name { get; set; } = $"Duckdb DuckDBConnection {CoreStrings.S_ObjectPool}";
|
||||
public int PoolSize { get; set; } = 1;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public bool IsAutoDisposeWithSystem { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 2;
|
||||
public int Weight { get; set; } = 1;
|
||||
public string[] Attaches = new string[0];
|
||||
|
||||
private string _connectionString;
|
||||
public string ConnectionString
|
||||
{
|
||||
get => _connectionString;
|
||||
set
|
||||
{
|
||||
_connectionString = value ?? "";
|
||||
|
||||
PoolSize = 1;
|
||||
var minPoolSize = 1;
|
||||
|
||||
if (Regex.IsMatch(_connectionString, @"ACCESS_MODE\s*=\s*READ_ONLY", RegexOptions.IgnoreCase))
|
||||
{
|
||||
//One process can both read and write to the database.
|
||||
//Multiple processes can read from the database, but no processes can write (access_mode = 'READ_ONLY').
|
||||
|
||||
var pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||
var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
PoolSize = 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.OpenAndAttach(Attaches);
|
||||
return obj.Value.Ping(true);
|
||||
}
|
||||
|
||||
public DbConnection OnCreate() => DuckdbConnectionPool.CreateConnection(_connectionString);
|
||||
|
||||
public void OnDestroy(DbConnection obj)
|
||||
{
|
||||
if (obj.State != ConnectionState.Closed) obj.Close();
|
||||
obj.Dispose();
|
||||
}
|
||||
|
||||
public void OnGet(Object<DbConnection> obj)
|
||||
{
|
||||
|
||||
if (_pool.IsAvailable)
|
||||
{
|
||||
if (obj.Value == null)
|
||||
throw new Exception(CoreStrings.S_ConnectionStringError_CheckProjectConnection(this.Name));
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open)
|
||||
obj.Value.OpenAndAttach(Attaches);
|
||||
}
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
async public Task OnGetAsync(Object<DbConnection> obj)
|
||||
{
|
||||
|
||||
if (_pool.IsAvailable)
|
||||
{
|
||||
if (obj.Value == null)
|
||||
throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open)
|
||||
await obj.Value.OpenAndAttachAsync(Attaches);
|
||||
}
|
||||
}
|
||||
#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
|
||||
{
|
||||
using (var cmd = PingCommand(that))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static void OpenAndAttach(this DbConnection that, string[] attach)
|
||||
{
|
||||
that.Open();
|
||||
|
||||
if (attach?.Any() == true)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var att in attach)
|
||||
sb.Append($"attach database [{att}] as [{att.Split('/', '\\').Last().Split('.').First()}];\r\n");
|
||||
|
||||
var cmd = that.CreateCommand();
|
||||
cmd.CommandText = sb.ToString();
|
||||
cmd.ExecuteNonQuery();
|
||||
cmd.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var cmd = PingCommand(that))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async public static Task OpenAndAttachAsync(this DbConnection that, string[] attach)
|
||||
{
|
||||
await that.OpenAsync();
|
||||
|
||||
if (attach?.Any() == true)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var att in attach)
|
||||
sb.Append($"attach database [{att}] as [{att.Split('/', '\\').Last().Split('.').First()}];\r\n");
|
||||
|
||||
var cmd = that.CreateCommand();
|
||||
cmd.CommandText = sb.ToString();
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
cmd.Dispose();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
216
Providers/FreeSql.Provider.Duckdb/DuckdbCodeFirst.cs
Normal file
216
Providers/FreeSql.Provider.Duckdb/DuckdbCodeFirst.cs
Normal file
@ -0,0 +1,216 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using DuckDB.NET.Native;
|
||||
using System.Collections;
|
||||
|
||||
namespace FreeSql.Duckdb
|
||||
{
|
||||
|
||||
class DuckdbCodeFirst : Internal.CommonProvider.CodeFirstProvider
|
||||
{
|
||||
|
||||
public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
|
||||
public DuckdbCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, CsToDb<DuckDBType>> _dicCsToDb = new Dictionary<string, CsToDb<DuckDBType>>() {
|
||||
{ typeof(bool).FullName, CsToDb.New(DuckDBType.Boolean, "boolean","boolean NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DuckDBType.Boolean, "boolean","boolean", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, CsToDb.New(DuckDBType.TinyInt, "tinyint", "tinyint NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DuckDBType.TinyInt, "tinyint", "tinyint", false, true, null) },
|
||||
{ typeof(short).FullName, CsToDb.New(DuckDBType.SmallInt, "smallint","smallint NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DuckDBType.SmallInt, "smallint", "smallint", false, true, null) },
|
||||
{ typeof(int).FullName, CsToDb.New(DuckDBType.Integer, "integer", "integer NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DuckDBType.Integer, "integer", "integer", false, true, null) },
|
||||
{ typeof(long).FullName, CsToDb.New(DuckDBType.BigInt, "bigint","bigint NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DuckDBType.BigInt, "bigint","bigint", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, CsToDb.New(DuckDBType.UnsignedTinyInt, "utinyint","utinyint NOT NULL", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DuckDBType.UnsignedTinyInt, "utinyint","utinyint", true, true, null) },
|
||||
{ typeof(ushort).FullName, CsToDb.New(DuckDBType.UnsignedSmallInt, "usmallint","usmallint NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DuckDBType.UnsignedSmallInt, "usmallint", "usmallint", true, true, null) },
|
||||
{ typeof(uint).FullName, CsToDb.New(DuckDBType.UnsignedInteger, "uinteger", "uinteger NOT NULL", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DuckDBType.UnsignedInteger, "uinteger", "uinteger", true, true, null) },
|
||||
{ typeof(ulong).FullName, CsToDb.New(DuckDBType.UnsignedBigInt, "ubigint", "ubigint NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DuckDBType.UnsignedBigInt, "ubigint", "ubigint", true, true, null) },
|
||||
|
||||
{ typeof(double).FullName, CsToDb.New(DuckDBType.Double, "double", "double NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DuckDBType.Double, "double", "double", false, true, null) },
|
||||
{ typeof(float).FullName, CsToDb.New(DuckDBType.Float, "float","float NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DuckDBType.Float, "float","float", false, true, null) },
|
||||
{ typeof(decimal).FullName, CsToDb.New(DuckDBType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DuckDBType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, CsToDb.New(DuckDBType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(DuckDBType.Time, "time", "time",false, true, null) },
|
||||
{ typeof(DateTime).FullName, CsToDb.New(DuckDBType.Timestamp, "timestamp", "timestamp NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DuckDBType.Timestamp, "timestamp", "timestamp", false, true, null) },
|
||||
|
||||
#if net60
|
||||
{ typeof(TimeOnly).FullName, CsToDb.New(DuckDBType.Time, "time", "time NOT NULL", false, false, 0) },{ typeof(TimeOnly?).FullName, CsToDb.New(DuckDBType.Time, "time", "time", false, true, null) },
|
||||
{ typeof(DateOnly).FullName, CsToDb.New(DuckDBType.Date, "date", "date NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateOnly?).FullName, CsToDb.New(DuckDBType.Date, "date", "date", false, true, null) },
|
||||
#endif
|
||||
|
||||
{ typeof(byte[]).FullName, CsToDb.New(DuckDBType.Blob, "blob", "blob", false, null, new byte[0]) },
|
||||
{ typeof(string).FullName, CsToDb.New(DuckDBType.Varchar, "varchar", "varchar(255)", false, null, "") },
|
||||
{ typeof(char).FullName, CsToDb.New(DuckDBType.Varchar, "char", "char(1)", false, null, '\0') },
|
||||
|
||||
{ typeof(Guid).FullName, CsToDb.New(DuckDBType.Uuid, "uuid", "uuid NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(DuckDBType.Uuid, "uuid", "uuid", false, true, null) },
|
||||
|
||||
{ typeof(BitArray).FullName, CsToDb.New(DuckDBType.Bit, "bit", "bit", false, null, new BitArray(new byte[64])) },
|
||||
{ typeof(BigInteger).FullName, CsToDb.New(DuckDBType.HugeInt, "hugeint","hugeint NOT NULL", false, false, 0) },{ typeof(BigInteger?).FullName, CsToDb.New(DuckDBType.HugeInt, "hugeint","hugeint", false, true, null) },
|
||||
};
|
||||
|
||||
public override DbInfoResult GetDbInfo(Type type)
|
||||
{
|
||||
var isarray = type.FullName != "System.Byte[]" && type.IsArray;
|
||||
var elementType = isarray ? type.GetElementType() : type;
|
||||
var info = GetDbInfoNoneArray(elementType);
|
||||
if (info == null) return null;
|
||||
if (isarray == false) return new DbInfoResult((int)info.type, info.dbtype, info.dbtypeFull, info.isnullable, info.defaultValue);
|
||||
var dbtypefull = Regex.Replace(info.dbtypeFull, $@"{info.dbtype}(\s*\([^\)]+\))?", "$0[]").Replace(" NOT NULL", "");
|
||||
return new DbInfoResult((int)(info.type | DuckDBType.Array), $"{info.dbtype}[]", dbtypefull, null, Array.CreateInstance(elementType, 0));
|
||||
}
|
||||
CsToDb<DuckDBType> GetDbInfoNoneArray(Type type)
|
||||
{
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return trydc;
|
||||
if (type.IsArray) return null;
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.IsNullableType() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null)
|
||||
{
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
CsToDb.New(DuckDBType.BigInt, "bigint", $"bigint{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()) :
|
||||
CsToDb.New(DuckDBType.Integer, "integer", $"integer{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue());
|
||||
if (_dicCsToDb.ContainsKey(type.FullName) == false)
|
||||
{
|
||||
lock (_dicCsToDbLock)
|
||||
{
|
||||
if (_dicCsToDb.ContainsKey(type.FullName) == false)
|
||||
_dicCsToDb.Add(type.FullName, newItem);
|
||||
}
|
||||
}
|
||||
return newItem;
|
||||
}
|
||||
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 = _commonUtils.SplitTableName(tb.DbName);
|
||||
if (tbname?.Length == 1) tbname = new[] { "main", tbname[0] };
|
||||
|
||||
var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { "main", tboldname[0] };
|
||||
if (string.IsNullOrEmpty(obj.tableName) == false)
|
||||
{
|
||||
var tbtmpname = _commonUtils.SplitTableName(obj.tableName);
|
||||
if (tbtmpname?.Length == 1) tbtmpname = new[] { "main", tbtmpname[0] };
|
||||
if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1])
|
||||
{
|
||||
tbname = tbtmpname;
|
||||
tboldname = null;
|
||||
}
|
||||
}
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, $" select 1 from {tbname[0]}.sqlite_master where type='table' and name='{tbname[1]}'") == null)
|
||||
{ //表不存在
|
||||
if (tboldname != null)
|
||||
{
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, $" select 1 from {tboldname[0]}.sqlite_master where type='table' and name='{tboldname[1]}'") == null)
|
||||
//模式或表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
if (tboldname == null)
|
||||
{
|
||||
//创建表
|
||||
var createTableName = _commonUtils.QuoteSqlName(tbname[0], tbname[1]);
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(createTableName).Append(" ( ");
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
{
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsIdentity == true)
|
||||
seqcols.Add(NativeTuple.Create(tbcol, tbname, true));
|
||||
sb.Append(",");
|
||||
}
|
||||
if (tb.Primarys.Any())
|
||||
{
|
||||
sb.Append(" \r\n PRIMARY KEY (");
|
||||
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) \r\n;\r\n");
|
||||
//创建表的索引
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
sb.Append("CREATE ");
|
||||
if (uk.IsUnique) sb.Append("UNIQUE ");
|
||||
sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(tbname[0], ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(tbname[1]).Append("(");
|
||||
foreach (var tbcol in uk.Columns)
|
||||
{
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
if (tbcol.IsDesc) sb.Append(" DESC");
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(");\r\n");
|
||||
}
|
||||
//创建表的索引
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
sb.Append("CREATE ");
|
||||
if (uk.IsUnique) sb.Append("UNIQUE ");
|
||||
sb.Append("INDEX ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(createTableName);
|
||||
sb.Append("(");
|
||||
foreach (var tbcol in uk.Columns)
|
||||
{
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
if (tbcol.IsDesc) sb.Append(" DESC");
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(");\r\n");
|
||||
}
|
||||
//备注
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
{
|
||||
if (string.IsNullOrEmpty(tbcol.Comment) == false)
|
||||
sb.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n");
|
||||
}
|
||||
if (string.IsNullOrEmpty(tb.Comment) == false)
|
||||
sb.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\r\n");
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表在一个模式下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1])).Append(" RENAME TO \"").Append(tbname[1]).Append("\";\r\n");
|
||||
else
|
||||
{
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
}
|
||||
}
|
||||
else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
}
|
||||
foreach (var seqcol in seqcols)
|
||||
{
|
||||
var tbname = seqcol.Item2;
|
||||
var seqname = Internal.Utils.GetCsName($"{tbname[0]}.{tbname[1]}_{seqcol.Item1.Attribute.Name}_sequence_name").ToLower();
|
||||
var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
|
||||
var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name);
|
||||
sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT null;\r\n");
|
||||
sb.Append("DROP SEQUENCE IF EXISTS ").Append(seqname).Append(";\r\n");
|
||||
if (seqcol.Item3)
|
||||
{
|
||||
sb.Append("CREATE SEQUENCE ").Append(seqname).Append(";\r\n");
|
||||
sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT nextval('").Append(seqname).Append("');\r\n");
|
||||
//sb.Append(" SELECT case when max(").Append(colname2).Append(") is null then 0 else setval('").Append(seqname).Append("', max(").Append(colname2).Append(")) end FROM ").Append(tbname2).Append(";\r\n");
|
||||
}
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
370
Providers/FreeSql.Provider.Duckdb/DuckdbDbFirst.cs
Normal file
370
Providers/FreeSql.Provider.Duckdb/DuckdbDbFirst.cs
Normal file
@ -0,0 +1,370 @@
|
||||
using DuckDB.NET.Native;
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.Duckdb
|
||||
{
|
||||
class DuckdbDbFirst : IDbFirst
|
||||
{
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public DuckdbDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
{
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public int GetDbType(DbColumnInfo column) => (int)GetSqlDbType(column);
|
||||
DuckDBType GetSqlDbType(DbColumnInfo column)
|
||||
{
|
||||
var dbtype = column.DbTypeText ?? "";
|
||||
var isarray = Regex.IsMatch(dbtype, @"\[\d*\]$") == true;
|
||||
if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2);
|
||||
DuckDBType ret = DuckDBType.Invalid;
|
||||
switch (dbtype.ToLower())
|
||||
{
|
||||
case "bigint":
|
||||
case "int8":
|
||||
case "long":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["bigint"]);
|
||||
ret = DuckDBType.BigInt; break;
|
||||
case "ubigint": ret = DuckDBType.UnsignedBigInt; break;
|
||||
case "bit":
|
||||
case "bitstring": ret = DuckDBType.Bit; break;
|
||||
case "blob":
|
||||
case "bytea":
|
||||
case "binary":
|
||||
case "varbinary":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["blob"]);
|
||||
ret = DuckDBType.Blob; break;
|
||||
case "boolean":
|
||||
case "bool":
|
||||
case "logical":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["boolean"]);
|
||||
ret = DuckDBType.Boolean; break;
|
||||
case "date":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["timestamp"]);
|
||||
ret = DuckDBType.Date; break;
|
||||
case "decimal":
|
||||
case "numeric":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["decimal(10,2)"]);
|
||||
ret = DuckDBType.Decimal; break;
|
||||
case "double":
|
||||
case "float8":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["double"]);
|
||||
ret = DuckDBType.Double; break;
|
||||
case "float":
|
||||
case "float4":
|
||||
case "real":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["float"]);
|
||||
ret = DuckDBType.Float; break;
|
||||
case "hugeint": ret = DuckDBType.HugeInt; break;
|
||||
case "uhugeint":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["hugeint"]);
|
||||
ret = DuckDBType.UnsignedHugeInt; break;
|
||||
case "integer":
|
||||
case "int4":
|
||||
case "int":
|
||||
case "signed":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["integer"]);
|
||||
ret = DuckDBType.Integer; break;
|
||||
case "uinteger": ret = DuckDBType.UnsignedInteger; break;
|
||||
case "interval":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["time"]);
|
||||
ret = DuckDBType.Interval; break;
|
||||
case "smallint":
|
||||
case "int2":
|
||||
case "short":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["smallint"]);
|
||||
ret = DuckDBType.SmallInt; break;
|
||||
case "usmallint": ret = DuckDBType.UnsignedSmallInt; break;
|
||||
case "time": ret = DuckDBType.Time; break;
|
||||
case "timestamp with time zone":
|
||||
case "timestamptz":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["timestamp"]);
|
||||
ret = DuckDBType.TimestampTz; break;
|
||||
case "timestamp":
|
||||
case "datetime":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["timestamp"]);
|
||||
ret = DuckDBType.Timestamp; break;
|
||||
case "tinyint":
|
||||
case "int1":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["tinyint"]);
|
||||
ret = DuckDBType.TinyInt; break;
|
||||
case "utinyint": ret = DuckDBType.UnsignedTinyInt; break;
|
||||
case "uuid": ret = DuckDBType.Uuid; break;
|
||||
case "varchar":
|
||||
case "char":
|
||||
case "bpchar":
|
||||
case "text":
|
||||
case "string":
|
||||
_dicDbToCs.TryAdd(dbtype, _dicDbToCs["varchar(255)"]);
|
||||
ret = DuckDBType.Varchar; break;
|
||||
default:
|
||||
if (dbtype.StartsWith("struct("))
|
||||
ret = DuckDBType.Struct;
|
||||
else if (dbtype.StartsWith("map("))
|
||||
ret = DuckDBType.Map;
|
||||
else if (dbtype.StartsWith("union("))
|
||||
ret = DuckDBType.Union;
|
||||
break;
|
||||
}
|
||||
return isarray ? (ret | DuckDBType.Array) : ret;
|
||||
}
|
||||
|
||||
static ConcurrentDictionary<string, DbToCs> _dicDbToCs = new ConcurrentDictionary<string, DbToCs>(StringComparer.CurrentCultureIgnoreCase);
|
||||
static DuckdbDbFirst()
|
||||
{
|
||||
var defaultDbToCs = new Dictionary<string, DbToCs>() {
|
||||
{ "boolean", new DbToCs("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
|
||||
|
||||
{ "tinyint", new DbToCs("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetByte") },
|
||||
{ "smallint", new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||
{ "integer", new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ "bigint", new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ "utinyint", new DbToCs("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt16") },
|
||||
{ "usmallint", new DbToCs("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt32") },
|
||||
{ "uinteger", new DbToCs("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt64") },
|
||||
{ "ubigint", new DbToCs("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ "double", new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||
{ "float", new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||
{ "decimal(10,2)", new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ "time", new DbToCs("(TimeSpan?)", "TimeSpan.FromSeconds(long.Parse({0}))", "{0}.TotalSeconds.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ "timestamp", new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") },
|
||||
|
||||
{ "blob", new DbToCs("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ "varchar(255)", new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ "uuid", new DbToCs("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}.Value", "GetGuid") },
|
||||
|
||||
{ "hugeint", new DbToCs("(BigInteger?)", "FreeSql.Internal.Utils.ToBigInteger({0})", "{0}.ToString()", "BigInteger?", typeof(BigInteger), typeof(BigInteger?), "{0}.Value", "GetValue") },
|
||||
};
|
||||
foreach (var kv in defaultDbToCs)
|
||||
_dicDbToCs.TryAdd(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
|
||||
public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csParse : null;
|
||||
public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csStringify : null;
|
||||
public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
|
||||
public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csTypeInfo : null;
|
||||
public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csTypeValue : null;
|
||||
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.dataReaderMethod : null;
|
||||
|
||||
public List<string> GetDatabases()
|
||||
{
|
||||
return _orm.Ado.ExecuteArray("PRAGMA database_list").Select(a => string.Concat(a[1])).ToList();
|
||||
}
|
||||
|
||||
public bool ExistsTable(string name, bool ignoreCase)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name)) return false;
|
||||
var tbname = _commonUtils.SplitTableName(name);
|
||||
if (tbname?.Length == 1) tbname = new[] { "main", tbname[0] };
|
||||
if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
|
||||
var sql = $@" select 1 from {_commonUtils.QuoteSqlName(tbname[0])}.sqlite_master where type='table' and {(ignoreCase ? "lower(tbl_name)" : "tbl_name")}={_commonUtils.FormatSql("{0}", tbname[1])}";
|
||||
return string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, sql)) == "1";
|
||||
}
|
||||
|
||||
public DbTableInfo GetTableByName(string name, bool ignoreCase = true) => GetTables(null, name, ignoreCase)?.FirstOrDefault();
|
||||
public List<DbTableInfo> GetTablesByDatabase(params string[] database) => GetTables(database, null, false);
|
||||
|
||||
public List<DbTableInfo> GetTables(string[] database, string tablename, bool ignoreCase)
|
||||
{
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<string, DbTableInfo>();
|
||||
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
|
||||
string[] tbname = null;
|
||||
if (string.IsNullOrEmpty(tablename) == false)
|
||||
{
|
||||
tbname = _commonUtils.SplitTableName(tablename);
|
||||
if (tbname?.Length == 1) tbname = new[] { "main", tbname[0] };
|
||||
if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
|
||||
database = new[] { tbname[0] };
|
||||
}
|
||||
else if (database == null || database.Any() == false)
|
||||
database = GetDatabases().ToArray();
|
||||
if (database.Any() == false) return loc1;
|
||||
|
||||
Action<object[], int> addColumn = (row, position) =>
|
||||
{
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string type = string.Concat(row[2]);
|
||||
//long max_length = long.Parse(string.Concat(row[3]));
|
||||
string sqlType = string.Concat(row[4]);
|
||||
var m_len = Regex.Match(sqlType, @"\w+\((\d+)");
|
||||
int max_length = m_len.Success ? int.Parse(m_len.Groups[1].Value) : -1;
|
||||
bool is_nullable = string.Concat(row[5]) == "1";
|
||||
bool is_identity = string.Concat(row[6]) == "1";
|
||||
bool is_primary = string.Concat(row[7]) == "1";
|
||||
string comment = string.Concat(row[8]);
|
||||
string defaultValue = string.Concat(row[9]);
|
||||
if (max_length == 0) max_length = -1;
|
||||
loc3[table_id].Add(column, new DbColumnInfo
|
||||
{
|
||||
Name = column,
|
||||
MaxLength = max_length,
|
||||
IsIdentity = is_identity,
|
||||
IsNullable = is_nullable,
|
||||
IsPrimary = is_primary,
|
||||
DbTypeText = type,
|
||||
DbTypeTextFull = sqlType,
|
||||
Table = loc2[table_id],
|
||||
Comment = comment,
|
||||
DefaultValue = defaultValue,
|
||||
Position = position
|
||||
});
|
||||
loc3[table_id][column].DbType = this.GetDbType(loc3[table_id][column]);
|
||||
loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
|
||||
};
|
||||
|
||||
foreach (var db in database)
|
||||
{
|
||||
var sql = $@"
|
||||
select
|
||||
'{db}.' || tbl_name,
|
||||
'{db}',
|
||||
tbl_name,
|
||||
'',
|
||||
'TABLE',
|
||||
sql
|
||||
from {db}.sqlite_master where type='table'{(tbname == null ? "" : $" and {(ignoreCase ? "lower(tbl_name)" : "tbl_name")}={_commonUtils.FormatSql("{0}", tbname[1])}")}";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) continue;
|
||||
|
||||
var loc6 = new List<string[]>();
|
||||
var loc66 = new List<string[]>();
|
||||
var loc6_1000 = new List<string>();
|
||||
var loc66_1000 = new List<string>();
|
||||
foreach (var row in ds)
|
||||
{
|
||||
var table_id = string.Concat(row[0]);
|
||||
var schema = string.Concat(row[1]);
|
||||
var table = string.Concat(row[2]);
|
||||
var comment = string.Concat(row[3]);
|
||||
var type = string.Concat(row[4]) == "VIEW" ? DbTableType.VIEW : DbTableType.TABLE;
|
||||
if (database.Length == 1)
|
||||
{
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
schema = "";
|
||||
}
|
||||
loc2.Add(table_id, new DbTableInfo { Id = table_id, Schema = schema, Name = table, Comment = comment, Type = type });
|
||||
loc3.Add(table_id, new Dictionary<string, DbColumnInfo>());
|
||||
switch (type)
|
||||
{
|
||||
case DbTableType.TABLE:
|
||||
case DbTableType.VIEW:
|
||||
loc6_1000.Add(table.Replace("'", "''"));
|
||||
if (loc6_1000.Count >= 999)
|
||||
{
|
||||
loc6.Add(loc6_1000.ToArray());
|
||||
loc6_1000.Clear();
|
||||
}
|
||||
break;
|
||||
case DbTableType.StoreProcedure:
|
||||
loc66_1000.Add(table.Replace("'", "''"));
|
||||
if (loc66_1000.Count >= 999)
|
||||
{
|
||||
loc66.Add(loc66_1000.ToArray());
|
||||
loc66_1000.Clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == DbTableType.TABLE && table != "sqlite_sequence")
|
||||
{
|
||||
var cols = _orm.Ado.ExecuteArray(CommandType.Text, $"PRAGMA table_info({_commonUtils.FormatSql("{0}", $"{db}.{table}")})");
|
||||
var position = 0;
|
||||
foreach (var col in cols)
|
||||
{
|
||||
var col_name = string.Concat(col[1]);
|
||||
var is_identity = string.Concat(col[4]).StartsWith("nextval('");
|
||||
|
||||
var ds2item = new object[10];
|
||||
ds2item[0] = table_id;
|
||||
ds2item[1] = col_name;
|
||||
ds2item[2] = Regex.Replace(string.Concat(col[2]), @"\(\d+(\b*,\b*\d+)?\)", "").ToUpper();
|
||||
ds2item[4] = string.Concat(col[2]).ToUpper();
|
||||
ds2item[5] = string.Concat(col[5]) == "False" && string.Concat(col[3]) == "False" ? 1 : 0;
|
||||
ds2item[6] = is_identity ? 1 : 0;
|
||||
ds2item[7] = string.Concat(col[5]) == "True" ? 1 : 0;
|
||||
ds2item[8] = "";
|
||||
ds2item[9] = string.Concat(col[4]);
|
||||
addColumn(ds2item, ++position);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loc6_1000.Count > 0) loc6.Add(loc6_1000.ToArray());
|
||||
if (loc66_1000.Count > 0) loc66.Add(loc66_1000.ToArray());
|
||||
|
||||
if (loc6.Count == 0) continue;
|
||||
}
|
||||
|
||||
foreach (var table_id in loc3.Keys)
|
||||
{
|
||||
foreach (var loc5 in loc3[table_id].Values)
|
||||
{
|
||||
loc2[table_id].Columns.Add(loc5);
|
||||
if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
|
||||
if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
foreach (var loc4 in loc2.Values)
|
||||
{
|
||||
//if (loc4.Primarys.Count == 0 && loc4.UniquesDict.Count > 0)
|
||||
//{
|
||||
// foreach (var loc5 in loc4.UniquesDict.First().Value.Columns)
|
||||
// {
|
||||
// loc5.Column.IsPrimary = true;
|
||||
// loc4.Primarys.Add(loc5.Column);
|
||||
// }
|
||||
//}
|
||||
loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc4.Columns.Sort((c1, c2) =>
|
||||
{
|
||||
int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
|
||||
if (compare == 0)
|
||||
{
|
||||
bool b1 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
|
||||
bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
|
||||
compare = b2.CompareTo(b1);
|
||||
}
|
||||
//if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
|
||||
return compare;
|
||||
});
|
||||
loc1.Add(loc4);
|
||||
}
|
||||
loc1.Sort((t1, t2) =>
|
||||
{
|
||||
var ret = t1.Schema.CompareTo(t2.Schema);
|
||||
if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
|
||||
return ret;
|
||||
});
|
||||
|
||||
loc2.Clear();
|
||||
loc3.Clear();
|
||||
return loc1;
|
||||
}
|
||||
|
||||
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
|
||||
{
|
||||
return new List<DbEnumInfo>();
|
||||
}
|
||||
}
|
||||
}
|
560
Providers/FreeSql.Provider.Duckdb/DuckdbExpression.cs
Normal file
560
Providers/FreeSql.Provider.Duckdb/DuckdbExpression.cs
Normal file
@ -0,0 +1,560 @@
|
||||
using FreeSql.Internal;
|
||||
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.Duckdb
|
||||
{
|
||||
class DuckdbExpression : CommonExpression
|
||||
{
|
||||
|
||||
public DuckdbExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc)
|
||||
{
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.NodeType)
|
||||
{
|
||||
case ExpressionType.ArrayLength:
|
||||
var arrOper = (exp as UnaryExpression)?.Operand;
|
||||
var arrOperExp = getExp(arrOper);
|
||||
if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"len([{arrOperExp.TrimStart('(').TrimEnd(')')}])";
|
||||
if (arrOper.Type == typeof(byte[])) return $"octet_length({getExp(arrOper)})";
|
||||
return $"case when {arrOperExp} is null then 0 else len({arrOperExp}) end";
|
||||
case ExpressionType.Convert:
|
||||
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||
var gentype = exp.Type.NullableTypeOrThis();
|
||||
if (gentype != operandExp.Type.NullableTypeOrThis())
|
||||
{
|
||||
switch (exp.Type.NullableTypeOrThis().ToString())
|
||||
{
|
||||
case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(operandExp)} as utinyint)";
|
||||
case "System.Char": return $"substr(cast({getExp(operandExp)} as char), 1, 1)";
|
||||
case "System.DateTime": return ExpressionConstDateTime(operandExp) ?? $"cast({getExp(operandExp)} as timestamp)";
|
||||
case "System.Decimal": return $"cast({getExp(operandExp)} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(operandExp)} as double)";
|
||||
case "System.Int16": return $"cast({getExp(operandExp)} as smallint)";
|
||||
case "System.Int32": return $"cast({getExp(operandExp)} as integer)";
|
||||
case "System.Int64": return $"cast({getExp(operandExp)} as bigint)";
|
||||
case "System.SByte": return $"cast({getExp(operandExp)} as tinyint)";
|
||||
case "System.Single": return $"cast({getExp(operandExp)} as float)";
|
||||
case "System.String": return $"cast({getExp(operandExp)} as text)";
|
||||
case "System.UInt16": return $"cast({getExp(operandExp)} as usmallint)";
|
||||
case "System.UInt32": return $"cast({getExp(operandExp)} as uinteger)";
|
||||
case "System.UInt64": return $"cast({getExp(operandExp)} as ubigint)";
|
||||
case "System.Guid": return $"cast({getExp(operandExp)} as uuid)";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch (callExp.Method.Name)
|
||||
{
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
|
||||
{
|
||||
case "System.Boolean": return $"({getExp(callExp.Arguments[0])} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as utinyint)";
|
||||
case "System.Char": return $"substr(cast({getExp(callExp.Arguments[0])} as char), 1, 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 decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as double)";
|
||||
case "System.Int16": return $"cast({getExp(callExp.Arguments[0])} as smallint)";
|
||||
case "System.Int32": return $"cast({getExp(callExp.Arguments[0])} as integer)";
|
||||
case "System.Int64": return $"cast({getExp(callExp.Arguments[0])} as bigint)";
|
||||
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as tinyint)";
|
||||
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as float)";
|
||||
case "System.UInt16": return $"cast({getExp(callExp.Arguments[0])} as usmallint)";
|
||||
case "System.UInt32": return $"cast({getExp(callExp.Arguments[0])} as uinteger)";
|
||||
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as ubigint)";
|
||||
case "System.Guid": return $"cast({getExp(callExp.Arguments[0])} as uuid)";
|
||||
}
|
||||
return null;
|
||||
case "NewGuid":
|
||||
return null;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "cast(random()*1000000000 as int)";
|
||||
return null;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "random()";
|
||||
return null;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "random()";
|
||||
return null;
|
||||
case "ToString":
|
||||
if (callExp.Object != null)
|
||||
{
|
||||
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 ? $"({getExp(callExp.Object)})::text" : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var objExp = callExp.Object;
|
||||
var objType = objExp?.Type;
|
||||
if (objType?.FullName == "System.Byte[]") return null;
|
||||
|
||||
var argIndex = 0;
|
||||
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable))
|
||||
{
|
||||
objExp = callExp.Arguments.FirstOrDefault();
|
||||
objType = objExp?.Type;
|
||||
argIndex++;
|
||||
|
||||
if (objType == typeof(string))
|
||||
{
|
||||
switch (callExp.Method.Name)
|
||||
{
|
||||
case "First":
|
||||
case "FirstOrDefault":
|
||||
return $"substring({getExp(callExp.Arguments[0])}, 1, 1)";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (objType == null) objType = callExp.Method.DeclaringType;
|
||||
if (objType != null || objType.IsArrayOrList())
|
||||
{
|
||||
string left = null;
|
||||
switch (callExp.Method.Name)
|
||||
{
|
||||
case "Any":
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"(case when {left} is null then 0 else len({left}) end > 0)";
|
||||
case "Contains":
|
||||
tsc.SetMapColumnTmp(null);
|
||||
var args1 = getExp(callExp.Arguments[argIndex]);
|
||||
var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
|
||||
var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
|
||||
tsc.isNotSetMapColumnTmp = true;
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
tsc.isNotSetMapColumnTmp = false;
|
||||
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
|
||||
if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
|
||||
//判断 in 或 array @> array
|
||||
if (left.StartsWith("[") && left.EndsWith("]"))
|
||||
return $"({args1}) in ({left.TrimStart('[').TrimEnd(']')})";
|
||||
if (left.StartsWith("(") && left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
|
||||
return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
|
||||
return $"list_contains({left}, {args1})";
|
||||
case "Concat":
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
var right2 = getExp(callExp.Arguments[argIndex]);
|
||||
if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"[{right2.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"list_concat({left}, {right2})";
|
||||
case "GetLength":
|
||||
case "GetLongLength":
|
||||
case "Length":
|
||||
case "Count":
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"case when {left} is null then 0 else len({left}) end";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.MemberAccess:
|
||||
var memExp = exp as MemberExpression;
|
||||
var memParentExp = memExp.Expression?.Type;
|
||||
if (memParentExp?.FullName == "System.Byte[]") return null;
|
||||
if (memParentExp != null)
|
||||
{
|
||||
if (memParentExp.IsArrayOrList())
|
||||
{
|
||||
var left = getExp(memExp.Expression);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
switch (memExp.Member.Name)
|
||||
{
|
||||
case "Length":
|
||||
case "Count": return $"case when {left} is null then 0 else len({left}) end";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var arrExp = exp as NewArrayExpression;
|
||||
var arrSb = new StringBuilder();
|
||||
arrSb.Append("[");
|
||||
for (var a = 0; a < arrExp.Expressions.Count; a++)
|
||||
{
|
||||
if (a > 0) arrSb.Append(",");
|
||||
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||
}
|
||||
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||
return arrSb.Append("]").ToString();
|
||||
case ExpressionType.ListInit:
|
||||
var listExp = exp as ListInitExpression;
|
||||
var listSb = new StringBuilder();
|
||||
listSb.Append("(");
|
||||
for (var a = 0; a < listExp.Initializers.Count; a++)
|
||||
{
|
||||
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||
if (a > 0) listSb.Append(",");
|
||||
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||
}
|
||||
if (listSb.Length == 1) listSb.Append("NULL");
|
||||
return listSb.Append(")").ToString();
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (typeof(IList).IsAssignableFrom(newExp.Type))
|
||||
{
|
||||
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||
return getExp(newExp.Arguments[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
|
||||
{
|
||||
if (exp.Expression == null)
|
||||
{
|
||||
switch (exp.Member.Name)
|
||||
{
|
||||
case "Empty": return "''";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name)
|
||||
{
|
||||
case "Length": return $"length({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
|
||||
{
|
||||
if (exp.Expression == null)
|
||||
{
|
||||
switch (exp.Member.Name)
|
||||
{
|
||||
case "Now": return _common.Now;
|
||||
case "UtcNow": return _common.NowUtc;
|
||||
case "Today": return "current_date";
|
||||
case "MinValue": return "timestamp '0001-01-01 00:00:00.000'";
|
||||
case "MaxValue": return "timestamp '9999-12-31 23:59:59.999'";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name)
|
||||
{
|
||||
case "Date": return $"({left})::date";
|
||||
case "TimeOfDay": return $"strftime({left},'%H:%M:%S')::time";
|
||||
case "DayOfWeek": return $"dayofweek({left})";
|
||||
case "Day": return $"day({left})";
|
||||
case "DayOfYear": return $"dayofyear({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 $"millisecond({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc)
|
||||
{
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null)
|
||||
{
|
||||
switch (exp.Method.Name)
|
||||
{
|
||||
case "IsNullOrEmpty":
|
||||
var arg1 = getExp(exp.Arguments[0]);
|
||||
return $"({arg1} is null or {arg1} = '')";
|
||||
case "IsNullOrWhiteSpace":
|
||||
var arg2 = getExp(exp.Arguments[0]);
|
||||
return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')";
|
||||
case "Concat":
|
||||
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($"({ExpressionLambdaToSql(a, tsc)})::text", "''")}||'";
|
||||
}).ToArray();
|
||||
return string.Format(ExpressionLambdaToSql(exp.Arguments[0], tsc), expArgs);
|
||||
case "Join":
|
||||
if (exp.IsStringJoin(out var tolistObjectExp, out var toListMethod, out var toListArgs1))
|
||||
{
|
||||
var newToListArgs0 = Expression.Call(tolistObjectExp, toListMethod,
|
||||
Expression.Lambda(
|
||||
Expression.Call(
|
||||
typeof(SqlExtExtensions).GetMethod("StringJoinPgsqlGroupConcat"),
|
||||
Expression.Convert(toListArgs1.Body, typeof(object)),
|
||||
Expression.Convert(exp.Arguments[0], typeof(object))),
|
||||
toListArgs1.Parameters));
|
||||
var newToListSql = getExp(newToListArgs0);
|
||||
return newToListSql;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var left = getExp(exp.Object);
|
||||
switch (exp.Method.Name)
|
||||
{
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var leftLike = exp.Object.NodeType == ExpressionType.MemberAccess ? left : $"({left})";
|
||||
var args0Value = getExp(exp.Arguments[0]);
|
||||
if (args0Value == "NULL") return $"{leftLike} IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"{left} ^@ ({args0Value})";
|
||||
if (args0Value.Contains("%"))
|
||||
{
|
||||
if (exp.Method.Name == "EndsWith") return $"strpos({left}, {args0Value}) = length({left})-length({args0Value})+1";
|
||||
return $"strpos({left}, {args0Value}) > 0";
|
||||
}
|
||||
if (exp.Method.Name == "EndsWith") return $"{leftLike} LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::text)")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"{leftLike} LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"{leftLike} LIKE ('%' || ({args0Value})::text || '%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring":
|
||||
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||
else substrArgs1 += "+1";
|
||||
if (exp.Arguments.Count == 1) return $"substring({left}, {substrArgs1})";
|
||||
return $"substring({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = getExp(exp.Arguments[0]);
|
||||
//if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") {
|
||||
// var locateArgs1 = getExp(exp.Arguments[1]);
|
||||
// if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
|
||||
// else locateArgs1 += "+1";
|
||||
// return $"(instr({left}, {indexOfFindStr}, {locateArgs1})-1)";
|
||||
//}
|
||||
return $"(strpos({left}, {indexOfFindStr})-1)";
|
||||
case "PadLeft":
|
||||
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])}, ' ')";
|
||||
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "PadRight":
|
||||
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])}, ' ')";
|
||||
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Trim":
|
||||
case "TrimStart":
|
||||
case "TrimEnd":
|
||||
if (exp.Arguments.Count == 0)
|
||||
{
|
||||
if (exp.Method.Name == "Trim") return $"trim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
|
||||
if (exp.Method.Name == "TrimEnd") return $"rtrim({left})";
|
||||
}
|
||||
var trimArg1 = "";
|
||||
var trimArg2 = "";
|
||||
foreach (var argsTrim02 in exp.Arguments)
|
||||
{
|
||||
var argsTrim01s = new[] { argsTrim02 };
|
||||
if (argsTrim02.NodeType == ExpressionType.NewArrayInit)
|
||||
{
|
||||
var arritem = argsTrim02 as NewArrayExpression;
|
||||
argsTrim01s = arritem.Expressions.ToArray();
|
||||
}
|
||||
foreach (var argsTrim01 in argsTrim01s)
|
||||
{
|
||||
var trimChr = getExp(argsTrim01).Trim('\'');
|
||||
if (trimChr.Length == 1) trimArg1 += trimChr;
|
||||
else trimArg2 += $" || ({trimChr})";
|
||||
}
|
||||
}
|
||||
if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
}
|
||||
}
|
||||
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])}, 2)";
|
||||
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 $"power({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||
case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Truncate": return $"trunc({getExp(exp.Arguments[0])})";
|
||||
}
|
||||
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 $"(epoch({getExp(exp.Arguments[0])})-epoch({getExp(exp.Arguments[1])}))";
|
||||
case "DaysInMonth": return $"day(last_day(cast({getExp(exp.Arguments[0])}||'-'||{getExp(exp.Arguments[1])}||'-01' as date)))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
|
||||
|
||||
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 "AddDays": return $"date_add({left},cast(({args1})||' days' as interval))";
|
||||
case "AddHours": return $"date_add({left},cast(({args1})||' hours' as interval))";
|
||||
case "AddMilliseconds": return $"date_add({left},cast(({args1})||' milliseconds' as interval))";
|
||||
case "AddMinutes": return $"date_add({left},cast(({args1})||' minutes' as interval))";
|
||||
case "AddMonths": return $"date_add({left},cast(({args1})||' months' as interval))";
|
||||
case "AddSeconds": return $"date_add({left},cast(({args1})||' seconds' as interval))";
|
||||
case "AddTicks": return $"date_add({left},cast((({args1})/10)||' microseconds' as interval))";
|
||||
case "AddYears": return $"date_add({left},cast(({args1})||' years' as interval))";
|
||||
case "Subtract":
|
||||
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
|
||||
{
|
||||
case "System.DateTime": return $"cast((epoch({left})-epoch({args1}))||' seconds' as interval)";
|
||||
case "System.TimeSpan": return $"date_add({left},{args1})";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = {args1})";
|
||||
case "CompareTo": return $"(epoch({left})-epoch({args1}))";
|
||||
case "ToString":
|
||||
if (exp.Arguments.Count == 0) return $"strftime({left},'%Y-%m-%d %H:%M:%S')";
|
||||
switch (args1)
|
||||
{
|
||||
case "'yyyy-MM-dd HH:mm:ss'": return $"strftime({left},'%Y-%m-%d %H:%M:%S')";
|
||||
case "'yyyy-MM-dd HH:mm'": return $"strftime({left},'%Y-%m-%d %H:%M')";
|
||||
case "'yyyy-MM-dd HH'": return $"strftime({left},'%Y-%m-%d %H')";
|
||||
case "'yyyy-MM-dd'": return $"strftime({left},'%Y-%m-%d')";
|
||||
case "'yyyy-MM'": return $"strftime({left},'%Y-%m')";
|
||||
case "'yyyyMMddHHmmss'": return $"strftime({left},'%Y%m%d%H%M%S')";
|
||||
case "'yyyyMMddHHmm'": return $"strftime({left},'%Y%m%d%H%M')";
|
||||
case "'yyyyMMddHH'": return $"strftime({left},'%Y%m%d%H')";
|
||||
case "'yyyyMMdd'": return $"strftime({left},'%Y%m%d')";
|
||||
case "'yyyyMM'": return $"strftime({left},'%Y%m')";
|
||||
case "'yyyy'": return $"strftime({left},'%Y')";
|
||||
case "'HH:mm:ss'": return $"strftime({left},'%H:%M:%S')";
|
||||
}
|
||||
args1 = Regex.Replace(args1, "(yyyy|MM|dd|HH|mm|ss)", m =>
|
||||
{
|
||||
switch (m.Groups[1].Value)
|
||||
{
|
||||
case "yyyy": return $"%Y";
|
||||
case "MM": return $"%_a1";
|
||||
case "dd": return $"%_a2";
|
||||
case "HH": return $"%_a3";
|
||||
case "mm": return $"%_a4";
|
||||
case "ss": return $"%S";
|
||||
}
|
||||
return m.Groups[0].Value;
|
||||
});
|
||||
var argsFinds = new[] { "%Y", "%_a1", "%_a2", "%_a3", "%_a4", "%S" };
|
||||
var argsSpts = Regex.Split(args1, "(yy|M|d|H|hh|h|m|s|tt|t)");
|
||||
for (var a = 0; a < argsSpts.Length; a++)
|
||||
{
|
||||
switch (argsSpts[a])
|
||||
{
|
||||
case "yy": argsSpts[a] = $"substr(strftime({left},'%Y'),3,2)"; break;
|
||||
case "M": argsSpts[a] = $"strftime({left},'%-m')"; break;
|
||||
case "d": argsSpts[a] = $"strftime({left},'%-d')"; break;
|
||||
case "H": argsSpts[a] = $"strftime({left},'%-H')"; break;
|
||||
case "hh": argsSpts[a] = $"strftime({left},'%I')"; break;
|
||||
case "h": argsSpts[a] = $"strftime({left},'%-I')"; break;
|
||||
case "m": argsSpts[a] = $"strftime({left},'%-M')"; break;
|
||||
case "s": argsSpts[a] = $"strftime({left},'%-S')"; break;
|
||||
case "tt": argsSpts[a] = $"strftime({left},'%p')"; break;
|
||||
case "t": argsSpts[a] = $"substr(strftime({left},'%p'),1,1)"; break;
|
||||
default:
|
||||
var argsSptsA = argsSpts[a];
|
||||
if (argsSptsA.StartsWith("'")) argsSptsA = argsSptsA.Substring(1);
|
||||
if (argsSptsA.EndsWith("'")) argsSptsA = argsSptsA.Remove(argsSptsA.Length - 1);
|
||||
argsSpts[a] = argsFinds.Any(m => argsSptsA.Contains(m)) ? $"strftime({left},'{argsSptsA}')" : $"'{argsSptsA}'";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (argsSpts.Length > 0) args1 = $"({string.Join(" || ", argsSpts.Where(a => a != "''"))})";
|
||||
return args1.Replace("%_a1", "%m").Replace("%_a2", "%d").Replace("%_a3", "%H").Replace("%_a4", "%M");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc)
|
||||
{
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null)
|
||||
{
|
||||
switch (exp.Method.Name)
|
||||
{
|
||||
case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
|
||||
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as utinyint)";
|
||||
case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as char), 1, 1)";
|
||||
case "ToDateTime": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as timestamp)";
|
||||
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
|
||||
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as double)";
|
||||
case "ToInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)";
|
||||
case "ToInt32": return $"cast({getExp(exp.Arguments[0])} as integer)";
|
||||
case "ToInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)";
|
||||
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as float)";
|
||||
case "ToString": return $"cast({getExp(exp.Arguments[0])} as text)";
|
||||
case "ToUInt16": return $"cast({getExp(exp.Arguments[0])} as usmallint)";
|
||||
case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as uinteger)";
|
||||
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as ubigint)";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
17
Providers/FreeSql.Provider.Duckdb/DuckdbExtensions.cs
Normal file
17
Providers/FreeSql.Provider.Duckdb/DuckdbExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using FreeSql;
|
||||
using FreeSql.Duckdb.Curd;
|
||||
|
||||
public static partial class FreeSqlDuckdbGlobalExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatDuckdb(this string that, params object[] args) => _duckdbAdo.Addslashes(that, args);
|
||||
static FreeSql.Duckdb.DuckdbAdo _duckdbAdo = new FreeSql.Duckdb.DuckdbAdo();
|
||||
}
|
54
Providers/FreeSql.Provider.Duckdb/DuckdbProvider.cs
Normal file
54
Providers/FreeSql.Provider.Duckdb/DuckdbProvider.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.Duckdb.Curd;
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Threading;
|
||||
using System.Collections;
|
||||
using System.Numerics;
|
||||
|
||||
namespace FreeSql.Duckdb
|
||||
{
|
||||
|
||||
public class DuckdbProvider<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;
|
||||
Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) });
|
||||
}
|
||||
}
|
||||
|
||||
public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) => new DuckdbSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public override IInsert<T1> CreateInsertProvider<T1>() => new DuckdbInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere) => new DuckdbUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere) => new DuckdbDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>() => new DuckdbInsertOrUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
|
||||
public DuckdbProvider(string masterConnectionString, string[] slaveConnectionString, Func<DbConnection> connectionFactory = null)
|
||||
{
|
||||
InitInternal();
|
||||
this.InternalCommonUtils = new DuckdbUtils(this);
|
||||
this.InternalCommonExpression = new DuckdbExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new DuckdbAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.CodeFirst = new DuckdbCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.DbFirst = new DuckdbDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
if (connectionFactory != null) this.CodeFirst.IsNoneCommandParameter = true;
|
||||
}
|
||||
|
||||
~DuckdbProvider() => this.Dispose();
|
||||
int _disposeCounter;
|
||||
public override void Dispose()
|
||||
{
|
||||
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
|
||||
(this.Ado as AdoProvider)?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
242
Providers/FreeSql.Provider.Duckdb/DuckdbUtils.cs
Normal file
242
Providers/FreeSql.Provider.Duckdb/DuckdbUtils.cs
Normal file
@ -0,0 +1,242 @@
|
||||
using DuckDB.NET.Data;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Numerics;
|
||||
using static DuckDB.NET.Native.NativeMethods;
|
||||
using System.Text;
|
||||
using System.Security.AccessControl;
|
||||
|
||||
namespace FreeSql.Duckdb
|
||||
{
|
||||
|
||||
class DuckdbUtils : CommonUtils
|
||||
{
|
||||
public DuckdbUtils(IFreeSql orm) : base(orm)
|
||||
{
|
||||
}
|
||||
|
||||
static Array getParamterArrayValue(Type arrayType, object value, object defaultValue)
|
||||
{
|
||||
var valueArr = value as Array;
|
||||
var len = valueArr.GetLength(0);
|
||||
var ret = Array.CreateInstance(arrayType, len);
|
||||
for (var a = 0; a < len; a++)
|
||||
{
|
||||
var item = valueArr.GetValue(a);
|
||||
ret.SetValue(item == null ? defaultValue : getParamterValue(item.GetType(), item, 1), a);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static Dictionary<string, Func<object, object>> dicGetParamterValue = new Dictionary<string, Func<object, object>> {
|
||||
{ typeof(uint).FullName, a => long.Parse(string.Concat(a)) }, { typeof(uint[]).FullName, a => getParamterArrayValue(typeof(long), a, 0) }, { typeof(uint?[]).FullName, a => getParamterArrayValue(typeof(long?), a, null) },
|
||||
{ typeof(ulong).FullName, a => decimal.Parse(string.Concat(a)) }, { typeof(ulong[]).FullName, a => getParamterArrayValue(typeof(decimal), a, 0) }, { typeof(ulong?[]).FullName, a => getParamterArrayValue(typeof(decimal?), a, null) },
|
||||
{ typeof(ushort).FullName, a => int.Parse(string.Concat(a)) }, { typeof(ushort[]).FullName, a => getParamterArrayValue(typeof(int), a, 0) }, { typeof(ushort?[]).FullName, a => getParamterArrayValue(typeof(int?), a, null) },
|
||||
{ typeof(byte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(byte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(byte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) },
|
||||
{ typeof(sbyte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(sbyte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(sbyte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) },
|
||||
{ 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) },
|
||||
};
|
||||
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 DuckDBParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
|
||||
//var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
//if (tp != null) ret.DbType = (DbType)tp.Value;
|
||||
//if (col != null)
|
||||
//{
|
||||
// var dbtype = (DbType)_orm.DbFirst.GetDbType(new DatabaseModel.DbColumnInfo { DbTypeText = col.DbTypeText });
|
||||
// if (dbtype != 0)
|
||||
// {
|
||||
// ret.DbType = 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<DuckDBParameter>(sql, obj, "$", (name, type, value) =>
|
||||
{
|
||||
if (value != null) value = getParamterValue(type, value);
|
||||
var ret = new DuckDBParameter { 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.DbType = (DbType)tp.Value; DuckDBParameter DbType 未对齐
|
||||
//}
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatDuckdb(args);
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
var nametrim = name[0].Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
if (nametrim.StartsWith("\"") && nametrim.EndsWith("\""))
|
||||
return nametrim;
|
||||
return $"\"{nametrim.Replace(".", "\".\"")}\"";
|
||||
}
|
||||
return $"\"{string.Join("\".\"", name)}\"";
|
||||
}
|
||||
public override string TrimQuoteSqlName(string name)
|
||||
{
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}";
|
||||
}
|
||||
public override string[] SplitTableName(string name) => GetSplitTableNames(name, '"', '"', 2);
|
||||
public override string QuoteParamterName(string name) => $"${name}";
|
||||
public override string IsNull(string sql, object value) => $"coalesce({sql}, {value})";
|
||||
public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}";
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
|
||||
public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}";
|
||||
public override string Now => "current_timestamp";
|
||||
public override string NowUtc => "current_timestamp";
|
||||
|
||||
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);
|
||||
var type2 = type;
|
||||
if (type2 == typeof(byte[])) return FormatSql("{0}", value, 1);
|
||||
//array
|
||||
if (value is Array)
|
||||
{
|
||||
var valueArr = value as Array;
|
||||
var eleType = type2.GetElementType();
|
||||
var eleTypeNullable = eleType.NullableTypeOrThis();
|
||||
var len = valueArr.GetLength(0);
|
||||
var sb = new StringBuilder().Append("[");
|
||||
for (var a = 0; a < len; a++)
|
||||
{
|
||||
var item = valueArr.GetValue(a);
|
||||
if (eleType != eleTypeNullable) item = Utils.GetDataReaderValue(eleTypeNullable, item);
|
||||
if (a > 0) sb.Append(",");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, eleType, item));
|
||||
}
|
||||
sb.Append("]");
|
||||
//var dbinfo = _orm.CodeFirst.GetDbInfo(type);
|
||||
//if (dbinfo != null) sb.Append("::").Append(dbinfo.dbtype);
|
||||
return sb.ToString();
|
||||
}
|
||||
if (type2.IsGenericType)
|
||||
{
|
||||
var typeDefinition = type2.GetGenericTypeDefinition();
|
||||
//list
|
||||
if (typeDefinition == typeof(List<>))
|
||||
{
|
||||
var valueArr = value as IList;
|
||||
var eleType = type2.GenericTypeArguments.FirstOrDefault().NullableTypeOrThis();
|
||||
var eleTypeNullable = eleType.NullableTypeOrThis();
|
||||
var len = valueArr.Count;
|
||||
var sb = new StringBuilder().Append("[");
|
||||
for (var a = 0; a < len; a++)
|
||||
{
|
||||
var item = valueArr[a];
|
||||
if (eleType != eleTypeNullable) item = Utils.GetDataReaderValue(eleTypeNullable, item);
|
||||
if (a > 0) sb.Append(",");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, eleType, item));
|
||||
}
|
||||
sb.Append("]");
|
||||
//var dbinfo = _orm.CodeFirst.GetDbInfo(type);
|
||||
//if (dbinfo != null) sb.Append("::").Append(dbinfo.dbtype);
|
||||
return sb.ToString();
|
||||
}
|
||||
//struct
|
||||
if (typeDefinition == typeof(Dictionary<string, object>))
|
||||
{
|
||||
var dict = value as Dictionary<string, object>;
|
||||
if (dict.Count == 0) return "NULL";
|
||||
var sb = new StringBuilder("{");
|
||||
var idx = 0;
|
||||
foreach (var key in dict.Keys)
|
||||
{
|
||||
var val = dict[key];
|
||||
if (val == null) continue;
|
||||
if (idx > 0) sb.Append(",");
|
||||
sb.Append("'").Append(FormatSql("{0}", val, 1)).Append("':");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, val.GetType(), val));
|
||||
idx++;
|
||||
}
|
||||
return sb.Append("}").ToString();
|
||||
}
|
||||
//map
|
||||
if (typeDefinition == typeof(Dictionary<,>))
|
||||
{
|
||||
var dict = value as IDictionary;
|
||||
var sb = new StringBuilder("map([");
|
||||
var idx = 0;
|
||||
Type tkey = null;
|
||||
foreach (var key in dict.Keys)
|
||||
{
|
||||
if (tkey == null) tkey = key.GetType();
|
||||
if (idx > 0) sb.Append(",");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, tkey, key));
|
||||
idx++;
|
||||
}
|
||||
sb.Append("],[");
|
||||
idx = 0;
|
||||
tkey = null;
|
||||
foreach (var val in dict.Values)
|
||||
{
|
||||
if (val == null) continue;
|
||||
if (tkey == null) tkey = val.GetType();
|
||||
if (idx > 0) sb.Append(",");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, tkey, val));
|
||||
idx++;
|
||||
}
|
||||
return sb.Append("])").ToString();
|
||||
}
|
||||
}
|
||||
if (type2 == typeof(BitArray))
|
||||
{
|
||||
var ba = value as BitArray;
|
||||
char[] ba1010 = new char[ba.Length];
|
||||
for (int a = 0; a < ba.Length; a++) ba1010[a] = ba[a] ? '1' : '0';
|
||||
return $"bit '{new string(ba1010)}'";
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>FreeSql;ncc;YeXiangQin</Authors>
|
||||
<Description>FreeSql + DuckDB is a fast in-process analytical database,supports .NetCore、.NetFramework4.6.1+</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM;duckdb</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.5.100-preview20240725</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="DuckDB.NET.Data.Full" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net6.0'">
|
||||
<DefineConstants>net60</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
BIN
Providers/FreeSql.Provider.Duckdb/key.snk
Normal file
BIN
Providers/FreeSql.Provider.Duckdb/key.snk
Normal file
Binary file not shown.
Reference in New Issue
Block a user