diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/FreeSql.Tests.Provider.Xugu.csproj b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/FreeSql.Tests.Provider.Xugu.csproj new file mode 100644 index 00000000..f359aef1 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/FreeSql.Tests.Provider.Xugu.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/UnitDbFirst.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/UnitDbFirst.cs new file mode 100644 index 00000000..24995607 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/UnitDbFirst.cs @@ -0,0 +1,44 @@ + +namespace FreeSql.Tests.Provider.Xugu +{ + public class UnitDbFirst + { + [Fact] + public void GetDatabases() + { + var t1 = g.xugu.DbFirst.GetDatabases(); + + Assert.True(t1.Count > 0); + } + + [Fact] + public void GetTablesByDatabase() + { + var t2 = g.xugu.DbFirst.GetTablesByDatabase(); + Assert.True(t2.Count > 0); + } + + [Fact] + public void GetTableByName() + { + var fsql = g.xugu; + var t1 = fsql.DbFirst.GetTableByName("GENERAL.system_log"); + Assert.NotNull(t1); + Assert.True(t1.Columns.Count > 0); + } + + [Fact] + public void ExistsTable() + { + var fsql = g.xugu; + Assert.False(fsql.DbFirst.ExistsTable("GENERAL.system_log")); + fsql.CodeFirst.SyncStructure(typeof(test_existstb01)); + Assert.True(fsql.DbFirst.ExistsTable("test_existstb01")); + fsql.Ado.ExecuteNonQuery("drop table test_existstb01"); + } + class test_existstb01 + { + public long id { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/Usings.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/Usings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/g.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/g.cs new file mode 100644 index 00000000..c70a65d6 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Xugu/g.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.Tests.Provider.Xugu +{ + public class g + { + + static Lazy xuguLazy = new Lazy(() => new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.Xugu, "IP=127.0.0.1;DB=SYSTEM;User=SYSDBA;PWD=SYSDBA;Port=5138;AUTO_COMMIT=on;CHAR_SET=UTF8") + //.UseAutoSyncStructure(true) + //.UseGenerateCommandParameterWithLambda(true) + .UseLazyLoading(true) + //.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper) + //.UseNoneCommandParameter(true) + + .UseMonitorCommand( + cmd => Trace.WriteLine("\r\n线程" + Thread.CurrentThread.ManagedThreadId + ": " + cmd.CommandText) //监听SQL命令对象,在执行前 + //, (cmd, traceLog) => Console.WriteLine(traceLog) + ) + .Build()); + public static IFreeSql xugu => xuguLazy.Value; + } +} diff --git a/FreeSql.sln b/FreeSql.sln index a15fde80..1e6e6788 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -121,6 +121,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.QuestDb", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.AggregateRoot", "Extensions\FreeSql.Extensions.AggregateRoot\FreeSql.Extensions.AggregateRoot.csproj", "{71A6F937-D11B-4AE4-9933-BB6B4D925665}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Xugu", "Providers\FreeSql.Provider.Xugu\FreeSql.Provider.Xugu.csproj", "{8064870C-22EA-4A58-972D-DBD57D096D91}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -719,6 +721,18 @@ Global {71A6F937-D11B-4AE4-9933-BB6B4D925665}.Release|x64.Build.0 = Release|Any CPU {71A6F937-D11B-4AE4-9933-BB6B4D925665}.Release|x86.ActiveCfg = Release|Any CPU {71A6F937-D11B-4AE4-9933-BB6B4D925665}.Release|x86.Build.0 = Release|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Debug|x64.ActiveCfg = Debug|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Debug|x64.Build.0 = Debug|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Debug|x86.ActiveCfg = Debug|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Debug|x86.Build.0 = Debug|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Release|Any CPU.Build.0 = Release|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Release|x64.ActiveCfg = Release|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Release|x64.Build.0 = Release|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Release|x86.ActiveCfg = Release|Any CPU + {8064870C-22EA-4A58-972D-DBD57D096D91}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -758,10 +772,11 @@ Global {ECF7FC70-A7FC-4FC3-9BF7-462EA0A65F9C} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {8A06B18A-A8BF-4AEA-AFE4-0F573C2DBFEE} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {71A6F937-D11B-4AE4-9933-BB6B4D925665} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} + {8064870C-22EA-4A58-972D-DBD57D096D91} = {2A381C57-2697-427B-9F10-55DA11FD02E4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - RESX_NeutralResourcesLanguage = en-US - RESX_PrefixTranslations = True SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98} + RESX_PrefixTranslations = True + RESX_NeutralResourcesLanguage = en-US EndGlobalSection EndGlobal diff --git a/Providers/FreeSql.Provider.Xugu/Curd/XuguDelete.cs b/Providers/FreeSql.Provider.Xugu/Curd/XuguDelete.cs new file mode 100644 index 00000000..d45ab9cc --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Curd/XuguDelete.cs @@ -0,0 +1,121 @@ +using FreeSql.Internal; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FreeSql.Xugu.Curd +{ + + class XuguDelete : Internal.CommonProvider.DeleteProvider + { + public XuguDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) + : base(orm, commonUtils, commonExpression, dywhere) + { + } + + public override List ExecuteDeleted() + { + var ret = new List(); + DbParameter[] dbParms = null; + StringBuilder sbret = null; + ToSqlFetch(sb => + { + if (dbParms == null) + { + dbParms = _params.ToArray(); + sbret = new StringBuilder(); + sbret.Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sbret.Append(", "); + sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + } + var sql = sb.Append(sbret).ToString(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + + Exception exception = null; + try + { + ret.AddRange(_orm.Ado.Query(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms)); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + }); + if (dbParms != null) + { + this.ClearData(); + sbret.Clear(); + } + return ret; + } + +#if net40 +#else + async public override Task> ExecuteDeletedAsync(CancellationToken cancellationToken = default) + { + var ret = new List(); + DbParameter[] dbParms = null; + StringBuilder sbret = null; + await ToSqlFetchAsync(async sb => + { + if (dbParms == null) + { + dbParms = _params.ToArray(); + sbret = new StringBuilder(); + sbret.Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sbret.Append(", "); + sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + } + var sql = sb.Append(sbret).ToString(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + + Exception exception = null; + try + { + ret.AddRange(await _orm.Ado.QueryAsync(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken)); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + }); + if (dbParms != null) + { + this.ClearData(); + sbret.Clear(); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.Xugu/Curd/XuguInsert.cs b/Providers/FreeSql.Provider.Xugu/Curd/XuguInsert.cs new file mode 100644 index 00000000..3520eea6 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Curd/XuguInsert.cs @@ -0,0 +1,242 @@ +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; +using XuguClient; + +namespace FreeSql.Xugu.Curd +{ + + class XuguInsert : Internal.CommonProvider.InsertProvider where T1 : class + { + public XuguInsert(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 InternalSource => _source; + internal Dictionary 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 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; + Aop.CurdBeforeEventArgs before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + Exception exception = null; + + + try + { + var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true); + if (identCols.Any() == false) + { + //var ex = new Exception("对没有自增主键的表执行插入操作时不能要求返回自增主键"); + //exception = ex; + _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params); + return 0; + + } + _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, cmd => + { + var rowid = (cmd as XGCommand).get_insert_rowid(); + + //using (var command = cmd.Connection.CreateCommand()) { + //command.CommandType = CommandType.Text; + var sqlIdentity = $"SELECT {_commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)} FROM {_table.DbName} WHERE \"ROWID\"='{rowid}'"; + + + //command.CommandText = sql; + if (!long.TryParse(_orm.Ado.ExecuteScalar(CommandType.Text, sqlIdentity, _params).ToString(), out ret)) + { + + }; + // command.Dispose(); + //} + + }, _params); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + + protected override List RawExecuteInserted() + { + + + + + + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return null; + + Aop.CurdBeforeEventArgs before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + Exception exception = null; + + var ret = new List(); + try + { + var sbColumn = new StringBuilder(); + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sbColumn.Append(", "); + sbColumn.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + + _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, cmd => + { + var rowid = (cmd as XGCommand).get_insert_rowid(); + + var sqlIdentity = $"SELECT {sbColumn} FROM {_table.DbName} WHERE \"ROWID\"='{rowid}'"; + + ret = _orm.Ado.Query(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sqlIdentity, _commandTimeout, _params); + + }, _params); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + + } + +#if net40 +#else + public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + public override Task ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + public override Task> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + + async protected override Task RawExecuteIdentityAsync(CancellationToken cancellationToken = default) + { + + + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + long ret = 0; + Exception exception = null; + try + { + var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true); + if (identCols.Any() == false) + { + //var ex = new Exception("对没有自增主键的表执行插入操作时不能要求返回自增主键"); + //exception = ex; + _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params); + return 0; + + } + await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, cmd => + { + var rowid = (cmd as XGCommand).get_insert_rowid(); + var sqlIdentity = $"SELECT {_commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)} FROM {_table.DbName} WHERE \"ROWID\"='{rowid}'"; + if (!long.TryParse(_orm.Ado.ExecuteScalar(CommandType.Text, sqlIdentity, _params).ToString(), out ret)) + { + + }; + return Task.CompletedTask; + }, _params, cancellationToken); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + + } + async protected override Task> RawExecuteInsertedAsync(CancellationToken cancellationToken = default) + { + + + + + + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return null; + + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + var sbColumn = new StringBuilder(); + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sbColumn.Append(", "); + sbColumn.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, cmd => + { + var rowid = (cmd as XGCommand).get_insert_rowid(); + + var sqlIdentity = $"SELECT {sbColumn} FROM {_table.DbName} WHERE \"ROWID\"='{rowid}'"; + + ret = _orm.Ado.Query(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sqlIdentity, _commandTimeout, _params); + return Task.CompletedTask; + }, _params, cancellationToken); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.Xugu/Curd/XuguInsertOrUpdate.cs b/Providers/FreeSql.Provider.Xugu/Curd/XuguInsertOrUpdate.cs new file mode 100644 index 00000000..0ba04315 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Curd/XuguInsertOrUpdate.cs @@ -0,0 +1,89 @@ +using FreeSql.Internal; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; + +namespace FreeSql.Xugu.Curd +{ + + class XuguInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public XuguInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + + var dbParams = new List(); + if (_sourceSql != null) return getMergeSql(null); + if (_source?.Any() != true) return null; + + var sqls = new string[2]; + // 如果实体类有自增属性,分成两个 List,有值的Item1 merge,无值的Item2 insert + var ds = SplitSourceByIdentityValueIsNull(_source); + if (ds.Item1.Any()) sqls[0] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item1.Select(a => getMergeSql(a))); + if (ds.Item2.Any()) sqls[1] = string.Join("\r\n\r\n;\r\n\r\n", ds.Item2.Select(a => getInsertSql(a))); + _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 getMergeSql(List data) + { + if (_tempPrimarys.Any() == false) throw new Exception(CoreStrings.InsertOrUpdate_Must_Primary_Key(_table.CsName)); + + var tempPrimaryIsIdentity = _tempPrimarys.Any(b => b.Attribute.IsIdentity); + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\nUSING ("); + WriteSourceSelectUnionAll(data, sb, dbParams); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _tempPrimarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => _tempPrimarys.Contains(a) == false && a.Attribute.CanUpdate == true && a.Attribute.IsIdentity == false && _updateIgnore.ContainsKey(a.Attribute.Name) == false); + if (_doNothing == false && cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion && a.Attribute.MapType != typeof(byte[]) ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values.Where(a => a.Attribute.CanInsert == true); + if (tempPrimaryIsIdentity == false) cols = cols.Where(a => a.Attribute.IsIdentity == false || string.IsNullOrEmpty(a.DbInsertValue) == false); + if (cols.Any()) + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => + { + //InsertValueSql = "seq.nextval" + if (tempPrimaryIsIdentity == false && a.Attribute.IsIdentity && string.IsNullOrEmpty(a.DbInsertValue) == false) return a.DbInsertValue; + return $"t2.{_commonUtils.QuoteSqlName(a.Attribute.Name)}"; + }))).Append(")"); + + return sb.ToString(); + } + string getInsertSql(List data) + { + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = data; + insert._table = _table; + var sql = insert.ToSql(); + if (string.IsNullOrEmpty(sql)) return null; + if (insert._params?.Any() == true) dbParams.AddRange(insert._params); + return sql; + } + + + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Xugu/Curd/XuguSelect.cs b/Providers/FreeSql.Provider.Xugu/Curd/XuguSelect.cs new file mode 100644 index 00000000..c1c0e289 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Curd/XuguSelect.cs @@ -0,0 +1,216 @@ +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.Xugu.Curd +{ + + class XuguSelect : FreeSql.Internal.CommonProvider.Select1Provider + { + + 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 _tables, List> tbUnions, Func _aliasRule, string _tosqlAppendContent, List _whereGlobalFilter, IFreeSql _orm) + { + if (_orm.CodeFirst.IsAutoSyncStructure) + _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray()); + + if (_whereGlobalFilter.Any()) + foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent)) + tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true); + + var sb = new StringBuilder(); + var tbUnionsGt0 = tbUnions.Count > 1; + for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++) + { + if (tbUnionsIdx > 0) sb.Append("\r\n \r\nUNION ALL\r\n \r\n"); + if (tbUnionsGt0) sb.Append(_select).Append(" * from ("); + var tbUnion = tbUnions[tbUnionsIdx]; + + var sbnav = new StringBuilder(); + sb.Append(_select); + if (_distinct) sb.Append("DISTINCT "); + sb.Append(field).Append(" \r\nFROM "); + var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray(); + var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray(); + for (var a = 0; a < tbsfrom.Length; a++) + { + sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias); + if (tbsjoin.Length > 0) + { + //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1 + for (var b = 1; b < tbsfrom.Length; b++) + { + sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias); + + if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1"); + else + { + var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On; + sb.Append(" ON ").Append(onSql); + if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false) + { + if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade); + else sb.Append(" AND ").Append(tbsfrom[b].Cascade); + } + } + } + break; + } + else + { + if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")"); + if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")"); + if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND ").Append(tbsfrom[a].Cascade); + } + if (a < tbsfrom.Length - 1) sb.Append(", "); + } + foreach (var tb in tbsjoin) + { + switch (tb.Type) + { + case SelectTableInfoType.Parent: + case SelectTableInfoType.RawJoin: + continue; + case SelectTableInfoType.LeftJoin: + sb.Append(" \r\nLEFT JOIN "); + break; + case SelectTableInfoType.InnerJoin: + sb.Append(" \r\nINNER JOIN "); + break; + case SelectTableInfoType.RightJoin: + sb.Append(" \r\nRIGHT JOIN "); + break; + } + sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition); + if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade); + if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")"); + } + if (_join.Length > 0) sb.Append(_join); + + sbnav.Append(_where); + if (!string.IsNullOrEmpty(_tables[0].Cascade)) + sbnav.Append(" AND ").Append(_tables[0].Cascade); + + if (sbnav.Length > 0) + { + sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5)); + } + if (string.IsNullOrEmpty(_groupby) == false) + { + sb.Append(_groupby); + if (string.IsNullOrEmpty(_having) == false) + 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 XuguSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override ISelect From(Expression, T2, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new PostgreSQLSelect(_orm, _commonUtils, _commonExpression, null); XuguSelect.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 PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select2Provider where T2 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select3Provider where T2 : class where T3 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select4Provider where T2 : class where T3 : class where T4 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select5Provider where T2 : class where T3 : class where T4 : class where T5 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select6Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select7Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select8Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class + { + public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select9Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select10Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select11Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select12Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select13Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select14Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select15Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class PostgreSQLSelect : FreeSql.Internal.CommonProvider.Select16Provider 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 PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => XuguSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } +} diff --git a/Providers/FreeSql.Provider.Xugu/Curd/XuguUpdate.cs b/Providers/FreeSql.Provider.Xugu/Curd/XuguUpdate.cs new file mode 100644 index 00000000..35ad0a2b --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Curd/XuguUpdate.cs @@ -0,0 +1,187 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FreeSql.Xugu.Curd +{ + + class XuguUpdate : Internal.CommonProvider.UpdateProvider + { + + public XuguUpdate(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 InternalIgnore => _ignore; + internal void InternalResetSource(List source) => _source = source; + internal string InternalWhereCaseSource(string CsName, Func thenValue) => WhereCaseSource(CsName, thenValue); + internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col); + + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + + protected override List RawExecuteUpdated() + { + var ret = new List(); + DbParameter[] dbParms = null; + StringBuilder sbret = null; + ToSqlFetch(sb => + { + if (dbParms == null) + { + dbParms = _params.Concat(_paramsSource).ToArray(); + sbret = new StringBuilder(); + sbret.Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sbret.Append(", "); + sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + } + var sql = sb.Append(sbret).ToString(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + + Exception exception = null; + try + { + var rettmp = _orm.Ado.Query(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms); + ValidateVersionAndThrow(rettmp.Count, sql, dbParms); + ret.AddRange(rettmp); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + }); + sbret?.Clear(); + return ret; + } + + protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) + { + if (primarys.Length == 1) + { + var pk = primarys.First(); + if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append("."); + caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name))); + return; + } + caseWhen.Append("("); + var pkidx = 0; + foreach (var pk in primarys) + { + if (pkidx > 0) caseWhen.Append(" || '+' || "); + if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append("."); + caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::text"); + ++pkidx; + } + caseWhen.Append(")"); + } + + protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) + { + if (primarys.Length == 1) + { + sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d))); + return; + } + sb.Append("("); + var pkidx = 0; + foreach (var pk in primarys) + { + if (pkidx > 0) sb.Append(" || '+' || "); + sb.Append(_commonUtils.FormatSql("{0}", pk.GetDbValue(d))).Append("::text"); + ++pkidx; + } + sb.Append(")"); + } + + protected override void ToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) + { + if (_noneParameter == false) return; + if (col.Attribute.MapType == typeof(string)) + { + sb.Append("::text"); + return; + } + var dbtype = _commonUtils.CodeFirst.GetDbInfo(col.Attribute.MapType)?.dbtype; + if (dbtype == null) return; + + sb.Append("::").Append(dbtype); + } + +#if net40 +#else + public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + public override Task> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + + async protected override Task> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default) + { + var ret = new List(); + DbParameter[] dbParms = null; + StringBuilder sbret = null; + await ToSqlFetchAsync(async sb => + { + if (dbParms == null) + { + dbParms = _params.Concat(_paramsSource).ToArray(); + sbret = new StringBuilder(); + sbret.Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sbret.Append(", "); + sbret.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + } + var sql = sb.Append(sbret).ToString(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + + Exception exception = null; + try + { + var rettmp = await _orm.Ado.QueryAsync(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); + ValidateVersionAndThrow(rettmp.Count, sql, dbParms); + ret.AddRange(rettmp); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + }); + sbret?.Clear(); + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/core/XuguClient.dll b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/core/XuguClient.dll new file mode 100644 index 00000000..1860eee1 Binary files /dev/null and b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/core/XuguClient.dll differ diff --git a/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/core/XuguClient.xml b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/core/XuguClient.xml new file mode 100644 index 00000000..45981201 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/core/XuguClient.xml @@ -0,0 +1,1713 @@ + + + + XuguClient + + + + + 摘要: + 表示要对数据源执行的 SQL 语句或存储过程。 + + + + + 摘要: + 是否在下面的查询语句使用服务器端游标 + 默认是不启用游标 + add 2016-02-04 + + + + + 摘要: + 获取 System.Data.Common.DbParameter 对象的集合。 有关参数的更多信息,请参见 配置参数和参数数据类型。 + 返回结果: + SQL 语句或存储过程的参数。 + + + + + 摘要: + 开启批量执行多行参数绑定标志 + !=0 时 启动多行绑定功能 + + + + + 摘要: + 获取或设置针对数据源运行的文本命令。 + 返回结果: + 要执行的文本命令。 默认值为空字符串 ("")。 + + + + + 摘要: + 获取或设置在终止执行命令的尝试并生成错误之前的等待时间。 + + 返回结果: + 等待命令执行的时间(以秒为单位)。 + + + + + 摘要: + 获取或设置此 XGCommand 使用的 XGConnection。 + 返回结果: + 与数据源的连接。 + + + + + 摘要: + 释放由XGCommand 使用的托管资源,还可以选择释放非托管资源。 + + + + + + 摘要: + 获取或设置将在其中执行此 System.Data.Common.DbCommand 对象的 System.Data.Common.DbTransaction。 + 返回结果: + 要在其中执行 .NET Framework 数据提供程序的 Command 对象的事务。 默认值为 null 引用(在 Visual Basic + 中为 Nothing)。 + + + + + 摘要: + 指示或指定如何解释 System.Data.Common.DbCommand.CommandText 属性。 + 返回结果: + System.Data.CommandType 值之一。 默认值为 Text。 + + + + + 摘要: + 获取或设置一个值,该值指示此命令对象在自定义界面控件中是否可见。 + 返回结果: + 如果该命令对象应在控件中可见,则为 true;否则为 false。 + + + + + 摘要: + 获取或设置命令结果在由 System.Data.Common.DbDataAdapter 的 Update 方法使用时如何应用于 System.Data.DataRow。 + + 返回结果: + System.Data.UpdateRowSource 值之一。 如果该命令不是自动生成的,则默认值为 Both。 否则,默认值为 None。 + + + + + 摘要: + 尝试取消 XGCommand 的执行。 + + + + + 摘要: + 在数据源上创建该命令的准备好的(或已编译的)版本。 + 该功能目前不可用 + + + + + 摘要: + 对连接对象执行 SQL 语句。 + 返回结果: + 受影响的行数。 + 异常: + System.InValidConnectionException: + 指定的连接无效或者尚未打开。 + System.commandtextException: + CommandText不能为空 + + + + + 摘要: + 针对 System.Data.Common.DbCommand.Connection 执行 System.Data.Common.DbCommand.CommandText,并返回 + XGDataReader。 + + 返回结果: + System.Data.Common.DbDataReader 对象。 + + + + + 摘要: + 针对 System.Data.Common.DbCommand.Connection 执行 System.Data.Common.DbCommand.CommandText,并使用 + System.Data.CommandBehavior 值之一返回 System.Data.Common.DbDataReader。 + + 参数: + behavior: + System.Data.CommandBehavior 值之一。 + + 返回结果: + XGDataReader 对象。 + + + + + 摘要: + 获取或设置将在其中执行此 System.Data.Common.DbCommand 对象的 System.Data.Common.DbCommand.DbTransaction。 + 返回结果: + 要在其中执行 .NET Framework 数据提供程序的 Command 对象的事务。 默认值为 null 引用(在 Visual Basic + 中为 Nothing)。 + + + + + 摘要: + 获取或设置此 System.Data.Common.DbCommand 使用的 System.Data.Common.DbConnection。 + 返回结果: + 与数据源的连接。 + + + + + 摘要: + 获取 System.Data.Common.DbParameter 对象的集合。 + 返回结果: + SQL 语句或存储过程的参数。 + + + + + 摘要: + 创建 System.Data.Common.DbParameter 对象的新实例。 + 返回结果: + System.Data.Common.DbParameter 对象。 + + + + + 摘要: + 对连接执行命令文本。 + 参数: + behavior: + System.Data.CommandBehavior 的一个实例。 + 返回结果: + 表示该操作的任务。 + 异常: + System.Data.Common.DbException: + 执行命令文本时出现的一个错误。 + System.ArgumentException: + System.Data.CommandBehavior 值无效。 + + + + + 摘要: + 获取或设置用于打开连接的字符串。 + 返回结果: + 用来建立初始连接的连接字符串。该连接字符串的确切内容取决于此连接的特定数据源。默认值为空字符串。 + + + + + 摘要: + 获取描述连接状态的字符串。 + + 返回结果: + 连接的状态。 返回的字符串的格式取决于您正在使用的连接的特定类型。 + + + + + 摘要: + 获取在建立连接时终止尝试并生成错误之前所等待的时间。 + 返回结果: + 等待连接打开的时间(以秒为单位)。 默认值是由您正在使用的连接的特定类型确定的。 + + + + + 摘要: + 在连接打开之后获取当前数据库的名称,或者在连接打开之前获取连接字符串中指定的数据库名。 + 返回结果: + 当前数据库的名称或连接打开后要使用的数据库的名称。 默认值为空字符串。 + + + + + 摘要: + 获取要连接的数据库服务器的名称。 + 返回结果: + 要连接的数据库服务器的名称。 默认值为空字符串。 + + + + + 摘要: + 获取表示对象所连接到的服务器的版本的字符串。 + 返回结果: + 数据库的版本。 返回的字符串的格式取决于您正在使用的连接的特定类型。 + 异常: + System.InValidConnectionException: + 连接尚未处于活动状态 + System.InvalidOperationException: + 当返回的任务尚未完成,且调用 Overload:System.Data.Common.DbConnection.OpenAsync 后连接并未打开时,将调用 + System.Data.Common.DbConnection.ServerVersion。 + + + + + 摘要: + 开始数据库事务。 + + 返回结果: + 表示新事务的对象。 + + + + + 摘要: + 以指定的隔离级别启动数据库事务。 + + 参数: + isolationLevel: + 指定事务的隔离级别。 + + 返回结果: + 表示新事务的对象。 + + + + + 摘要: + 为打开的连接更改当前数据库。 + + 参数: + databaseName: + 为要使用的连接指定数据库名称。 + + + + + 摘要: + 当事件状态更改时发生。 + + + + + 摘要: + 创建并返回与当前连接关联的 System.Data.Common.DbCommand 对象。 + + 返回结果: + System.Data.Common.DbCommand 对象。 + + + + + 摘要: + 返回此 System.Data.Common.DbConnection 的数据源的架构信息。 + 此版本驱动不支持此功能 + 返回结果: + 包含架构信息的 System.Data.DataTable。 + + + + + 摘要: + 使用表示架构名称的指定字符串返回此 System.Data.Common.DbConnection 的数据源的架构信息。 + + 参数: + collectionName: + 指定要返回的架构的名称。 + + 返回结果: + 包含架构信息的 System.Data.DataTable。 + + 异常: + System.ArgumentException: + collectionName 被指定为空。 + + + + + 摘要: + 使用表示架构名称的指定字符串以及表示限制值的指定字符串数组返回此 System.Data.Common.DbConnection 的数据源的架构信息。 + + 参数: + collectionName: + 指定要返回的架构的名称。 + + restrictionValues: + 为请求的架构指定一组限制值。 + + 返回结果: + 包含架构信息的 System.Data.DataTable。 + + 异常: + System.ArgumentException: + collectionName 被指定为空。 + + + + + 摘要: + 使用 System.Data.Common.DbConnection.ConnectionString 所指定的设置打开数据库连接。 + + + + + 摘要: + 关闭与数据库的连接。 此方法是关闭任何已打开连接的首选方法。 + + 异常: + System.Data.Common.DbException: + 在打开连接时出现连接级别的错误。 + + + + + 摘要: + 释放资源 + + + + + 关闭服务器端游标 + + + + + 摘要: + 开始数据库事务。 + + 参数: + isolationLevel: + 指定事务的隔离级别。 + + 返回结果: + 表示新事务的对象。 + + + + + 摘要: + 获取此 System.Data.Common.DbConnection 的 System.Data.Common.DbProviderFactory。 + 返回结果: + 一组方法的集合,这些方法用于创建提供程序对数据源类的实现的实例。 + + + + + 摘要: + 创建并返回与当前连接关联的 System.Data.Common.DbCommand 对象。 + + 返回结果: + System.Data.Common.DbCommand 对象。 + + + + + 摘要: + 引发 System.Data.Common.DbConnection.StateChange 事件。 + + 参数: + stateChange: + 包含事件数据的 System.Data.StateChangeEventArgs。 + + + + + 如果连接字符串在设计器中可见,则为 true;否则为 false。默认值为 true。 + + + + + DbConnectionStringBuilder 是否具有固定大小。 + + + + + DbConnectionStringBuilder 是否为只读。 + + + + + 返回 一个 System.Collections.ICollection 内部包含System.Data.Common.DbConnectionStringBuilder 中的键。 + + + + + 摘要: + 获取或设置与指定的键关联的值。 + + 参数: + keyword: + 要获取或设置的项的键。 + + 返回结果: + 与指定的键相关联的值。 如果指定键未找到,尝试让其返回空引用(在 Visual Basic 中为 Nothing),并尝试设置为使用指定键创建新的元素。 + 传递 null 键(在 Visual Basic 中为 Nothing)将引发 System.ArgumentNullException。 分配一个删除键/值对的 + null 值。 + + 异常: + T:System.ArgumentNullException: + keyword 为空引用(在 Visual Basic 中为 Nothing)。 + + T:System.NotSupportedException: + 设置了该属性,但 System.Data.Common.DbConnectionStringBuilder 为只读。 - 或 - 设置了该属性,但 keyword + 不存在于集合中,且 System.Data.Common.DbConnectionStringBuilder 具有固定的大小。 + + + + + 摘要: + 将带有指定键和值的条目添加到 System.Data.Common.DbConnectionStringBuilder 中。 + + 参数: + keyword: + 要添加到 System.Data.Common.DbConnectionStringBuilder 的键。 + + value: + 指定键的值。 + + 异常: + T:System.ArgumentNullException: + keyword 为空引用(在 Visual Basic 中为 Nothing)。 + + T:System.NotSupportedException: + System.Data.Common.DbConnectionStringBuilder 为只读。 - 或 - System.Data.Common.DbConnectionStringBuilder + 具有固定的大小。 + + + + + 摘要: + 清除 System.Data.Common.DbConnectionStringBuilder 实例的内容。 + + 异常: + T:System.NotSupportedException: + System.Data.Common.DbConnectionStringBuilder 为只读。 + + + + + 摘要: + 清除关联的 System.Data.Common.DbConnectionStringBuilder 上的 System.ComponentModel.PropertyDescriptor + 对象集合。 + 清除关联的 PropertyDescriptor 上的 DbConnectionStringBuilder 对象集合。 + + 注解 + 设计器使用此方法清除 DbConnectionStringBuilder上的所有关联属性说明符。 + + + + + 摘要: + 确定 XGConnectionStringBuilder 是否包含特定键。 + + 参数: + keyword: + 要在 XGConnectionStringBuilder 中定位的键。 + + 返回结果: + 如果 XGConnectionStringBuilder 包含具有指定键的项,则为 true;否则为 false。 + + 异常: + T:System.ArgumentNullException: + keyword 为空引用(在 Visual Basic 中为 Nothing)。 + + + + + 摘要: + 将此 System.Data.Common.DbConnectionStringBuilder 对象中的连接信息与提供的对象中的连接信息进行比较。 + + 参数: + connectionStringBuilder: + 要与此 System.Data.Common.DbConnectionStringBuilder 对象进行比较的 System.Data.Common.DbConnectionStringBuilder。 + + 返回结果: + 如果两个 System.Data.Common.DbConnectionStringBuilder 对象中的连接信息生成等效的连接字符串,则为 true;否则为 + false。 + + + + + 摘要: + 使用有关此 Hashtable 的所有属性的信息,填充提供的 DbConnectionStringBuilder。 + + 参数 + propertyDescriptors + Hashtable + 使用有关此 Hashtable 的信息填充的 DbConnectionStringBuilder。 + + + + + 摘要: + 从 System.Data.Common.DbConnectionStringBuilder 实例中移除具有指定的键的项。 + + 参数: + keyword: + 要从此 System.Data.Common.DbConnectionStringBuilder 中的连接字符串移除的键/值对中的键。 + + 返回结果: + 如果该键存在于连接字符串中并已被移除,则为 true;如果该键不存在,则为 false。 + + 异常: + T:System.ArgumentNullException: + keyword 为 Null(在 Visual Basic 中为 Nothing) + + T:System.NotSupportedException: + System.Data.Common.DbConnectionStringBuilder 是只读,或 System.Data.Common.DbConnectionStringBuilder + 具有固定的大小。 + + + + + 摘要: + 指示此 XGConnectionStringBuilder 实例中是否存在指定的键。 + 参数 + keyword + String + 要在 XGConnectionStringBuilder 中定位的键。 + 返回 + Boolean + 如果 true 包含具有指定键的项,则为 XGConnectionStringBuilder;否则为 false。 + 注解 + 此方法的行为与 ContainsKey 方法完全相同。 + + + + + 摘要: + 返回与此 System.Data.Common.DbConnectionStringBuilder 关联的连接字符串。 + + 返回结果: + 当前 System.Data.Common.DbConnectionStringBuilder.ConnectionString 属性。 + + + + + 摘要: + 从此 System.Data.Common.DbConnectionStringBuilder 中检索与提供的键相对应的值。 + + 参数: + keyword: + 要检索的项的键。 + + value: + 与 key 对应的值。 + + 返回结果: + 如果在连接字符串中找到 keyword,则为 true,否则为 false。 + + 异常: + T:System.ArgumentNullException: + keyword 包含一个 null 值(在 Visual Basic 中为 Nothing)。 + + + + + 摘要: + 用一个 SQL SELECT 语句和一个 XuguCsConnecion初始化 XGDataAdapter 类的新实例。 + selectCommandText: 一个字符串,表示将由 XGDataAdapter 的 XGDataAdapter.SelectCommand 属性使用的 SQL SELECT 语句或存储过程。 + selectConnection: 表示连接的 XGConnection。 + + + + + 摘要: + 使用 SQL SELECT 语句和连接字符串初始化 XGDataAdapter类的新实例。 + selectCommandText: 一个字符串,表示将由 XGDataAdapter 的 XGDataAdapter.SelectCommand 属性使用的 SQL SELECT 语句或存储过程。 + selectConnectionString: 连接字符串。 + + + + + 从查询SQL中获取完整表名(模式名.表名) + + 查询SQL + 表名 或 模式名.表名 + + + + 摘要: + 获取一个值,该值指示当前行的嵌套深度。 + + 返回结果: + 当前行的嵌套深度。 + + + + + 摘要: + 获取当前行中的列数。 + + 返回结果: + 当前行中的列数。 + + 异常: + System.NotSupportedException: + 没有对 SQL Server 实例的当前连接。 + + + + + 摘要: + 获取一个值,它指示此 XGDataReader是否包含一个或多个行。 + + 返回结果: + 如果 XGDataReader 包含一行或多行,则为 true;否则为 false。 + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 获取一个值,该值指示 XGDataReader 是否已关闭。 + + 返回结果: + 如果 XGDataReader 已关闭,则为 true;否则为 false。 + + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 通过执行 SQL 语句获取更改、插入或删除的行数。 + + 返回结果: + 更改、插入或删除的行数。 对于 SELECT 语句为 -1;如果没影响任何行或该语句失败,则为 0。 + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 获取指定列的作为 System.Object 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.IndexOutOfRangeException: + 传递的索引位于 0 至 System.Data.IDataRecord.FieldCount 的范围之外。 + + + + + 摘要: + 获取指定列的作为 System.Object 的实例的值。 + + 参数: + name: + 列的名称。 + + 返回结果: + 指定列的值。 + + 异常: + System.IndexOutOfRangeException: + 未找到具有指定名称的列。 + + + + + 摘要: + 关闭 XGDataReader 对象。 + + + + + 摘要: + 释放由 XGDataReader 类的当前实例占用的所有资源。 + + + + + 摘要: + 获取指定列的布尔值形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的字节形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 从指定列读取一个字节流(从 dataOffset 指示的位置开始),读到缓冲区中(从 bufferOffset 指示的位置开始)。 + + 参数: + ordinal: + 从零开始的列序号。 + + dataOffset: + 行中的索引,从其开始读取操作。 + + buffer: + 作为数据复制目标的缓冲区。 + + bufferOffset: + 具有作为数据复制目标的缓冲区的索引。 + + length: + 最多读取的字符数。 + + 返回结果: + 读取的实际字节数。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的单个字符串形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 从指定列读取一个字符流,从 dataOffset 指示的位置开始,读到缓冲区中,从 bufferOffset 指示的位置开始。 + + 参数: + ordinal: + 从零开始的列序号。 + + dataOffset: + 行中的索引,从其开始读取操作。 + + buffer: + 作为数据复制目标的缓冲区。 + + bufferOffset: + 具有作为数据复制目标的缓冲区的索引。 + + length: + 最多读取的字符数。 + + 返回结果: + 读取的实际字符数。 + + + + + 摘要: + 获取指定列的数据类型的名称。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 表示数据类型的名称的字符串。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 返回被请求的列序号的 System.Data.Common.DbDataReader 对象。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + System.Data.Common.DbDataReader 对象。 + + + + + 摘要: + 获取指定列的 System.DateTime 对象形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 System.Decimal 对象形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的双精度浮点数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 返回一个 System.Collections.IEnumerator,可用于循环访问数据读取器中的行。 + + 返回结果: + 一个 System.Collections.IEnumerator,可用于循环访问数据读取器中的行。 + + + + + 摘要: + 获取指定列的数据类型。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的数据类型。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的单精度浮点数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的全局唯一标识符 (GUID) 形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 16 位有符号整数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 32 位有符号整数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 64 位有符号整数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + System.IndexOutOfRangeException: + 指定的列序号i无效 + + + + + 摘要: + 给定了从零开始的列序号时,获取列的名称。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的名称。 + + + + + 摘要: + 获取表的名称。 + 返回结果: + 表的名称。 + + + + + 摘要: + 给定列名称时,获取列序号。 + + 参数: + name: + 列的名称。 + + 返回结果: + 从零开始的列序号。 + + 异常: + System.IndexOutOfRangeException: + 指定的名称不是有效的列名称。 + + + + + 摘要: + 返回一个 System.Data.DataTable,它描述 System.Data.Common.DbDataReader 的列元数据。 + + 返回结果: + 一个描述列元数据的 System.Data.DataTable。 + + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 获取指定列的作为 System.String 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的作为 XGClob 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的作为 XGBlob 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的作为 System.Object 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + + + + 摘要: + 使用当前行的列值来填充对象数组。 + + 参数: + values: + 要将特性列复制到其中的 System.Object 数组。 + + 返回结果: + 数组中 System.Object 的实例的数目。 + + + + + 摘要: + 获取一个值,该值指示列中是否包含不存在的或已丢失的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 如果指定的列与 System.DBNull 等效,则为 true;否则为 false。 + + + + + 摘要: + 读取批处理语句的结果时,使读取器前进到下一个结果。 + + 返回结果: + 如果存在多个结果集,则为 true;否则为 false。 + + + + + 摘要: + 将读取器XGDataReader 前进到结果集中的下一个记录。 + + 返回结果: + 如果存在多个行,则为 true;否则为 false。 + + + + + 摘要: + 释放由 XGDataReader 使用的托管资源,还可以选择释放非托管资源。 + + 参数: + disposing: + 若要释放托管资源和非托管资源,则为 true;若要仅释放非托管资源,则为 false。 + + + + + 无任何值(DBTYPE_EMPTY)。 + + + + + 布尔类型,其取值为:true、false或unknown。 + + + + + 固定长度字符串(SQL_CHAR)。它映射到 System.String。 + + + + + 8 位带符号的整数(DBTYPE_I1)。它映射到 System.SByte。 + + + + + 确切数值,其精度为 5,小数位数为 0(有符号:-32,768 <= n <= 32,767,没有符号:0 <= n <= 65,535)(SQL_SMALLINT)。它映射到 System.Int16。 + + + + + 确切数值,其精度为 10 和小数位数 0(有符号:-2[31] <= n <= 2[31] - 1,没有符号:0 <= n <= 2[32] - 1)(SQL_INTEGER)。它映射到 System.Int32。 + + + + + 确切数值,其精度为 19(如果有符号)或 20(如果没有符号),小数位数为 0(有符号:-2[63] <= n <= 2[63] - 1,没有符号:0 <= n <= 2[64] - 1)(SQL_BIGINT)。它映射到 System.Int64。 + + + + + 有符号的近似数值,其二进制精度为 24(零或绝对值为 10[-38] 到 10[38])(SQL_REAL)。它映射到 System.Single。 + + + + + 有符号的确切数值,其精度为 p,小数位数为 s,其中 1 <= p <= 15 并且 s <= p (SQL_NUMERIC)。该数据类型将映射到System.Decimal。 + + + + + 有符号的近似数值,其二进制精度为 53(零或绝对值为 10[-308] 到 10[308])(SQL_DOUBLE)。它映射到 System.Double。 + + + + + 日期数据,存储为双精度型(DBTYPE_DATE)。整数部分是自 1899 年 12 月 30 日以来的天数,而小数部分是不足一天的部分。它映射到System.DateTime。 + + + + + 格式为 yyyymmdd 的日期数据(SQL_TYPE_DATE)。它映射到 System.DateTime。 + + + + + 格式为 hhmmss 的日期数据(SQL_TYPE_TIMES)。它映射到 System.DateTime。 + + + + + 变长流字符串(SQL_CHAR)。它映射到 System.String。 + + + + + 二进制数据流 (SQL_BINARY)。它映射到 System.Byte 类型的 System.Array。 + + + + + 变长二进制。由用户设置该最大值 (SQL_VARBINARY)。它映射到 System.Byte 类型的 System.Array。 + + + + + 游标。可作为存储过程的输出参数类型和存储函数的返回值类型。 + + + + + 最大长度因数据源而定 (SQL_LONGVARCHAR)。它映射到 System.String。 + + + + + 长的二进制值(BLOB)。它映射到 System.Byte 类型的 System.Array。 + + + + + 根据DbType获取对应的XGDbType。 + + DbType + XGDbType + + + + 根据XGDbType获取对应的DbType。 + + XGDbType + DbType + + + + 根据值的Type和指定的XGDbType共同决定以获取对应的XGDbType。 + + 值的Type + XGDbType + 对应的XGDbType + + + + 根据数组类型获取对应的XGDbType。 + + 数组的Type + 参数的Type + 对应的XGDbType + + + + 用于参数绑定的类。 + + + + + 摘要: + 获取或设置参数的 System.Data.DbType。 + + 返回结果: + System.Data.DbType 值之一。 默认值为 System.Data.DbType.String。 + + 异常: + System.ArgumentException: + 该属性未设置为有效的 System.Data.DbType。 + + + + + 摘要: + 获取或设置该参数的值。 + + 返回结果: + 一个 System.Object,它是该参数的值。 默认值为 null。 + + + + + 创建作为当前实例副本的新对象。 + + 作为此实例副本的新对象。 + + + + 摘要: + 获取或设置一个值,该值指示参数是只可输入、只可输出、双向还是存储过程返回值参数。 + + 返回结果: + System.Data.ParameterDirection 值之一。 默认值为 Input。 + + 异常: + System.ArgumentException: + 该属性未设置为有效的 System.Data.ParameterDirection 值之一。 + + + + + 摘要: + 获取或设置列中数据的最大大小(以字节为单位)。 + + 返回结果: + 列中数据的最大大小(以字节为单位)。 默认值是从参数值推导出的。 + + + + + 无参的XGParameters构造方法。 + + + + + 通过参数名和参数值构造XGParameters对象。 + + 参数的名 + 参数的值 + + + + 通过XGParameters对象构造新的XGParameters副本。 + + 源XGParameters对象 + + + + 通过参数名和虚谷数据库的类型构造XGParameters对象。 + + 参数的名 + 虚谷数据库的类型 + + + + 通过参数名、虚谷数据库的类型和参数大小构造XGParameters对象。 + + 参数的名 + 虚谷数据库的类型 + 参数值所占字节大小 + + + + 构造XGParameters对象。 + + 参数的名 + 虚谷数据库的类型 + 参数值所占字节大小 + 参数的方向:Input,Output,InputOutput或ReturnValue + 无意义 + 无意义 + 无意义 + 无意义 + 无意义 + 参数的值 + + + + 摘要: + 获取或设置 System.Data.Common.DbParameter 的名称。 + + 返回结果: + System.Data.Common.DbParameter 的名称。 默认值为空字符串 ("")。 + + + + + 摘要: + 获取或设置源列的名称,该源列映射到 System.Data.DataSet 并用于加载或返回 System.Data.Common.DbParameter.Value。 + + 返回结果: + 映射到 System.Data.DataSet 的源列的名称。 默认值为空字符串。 + + + + + 摘要: + 设置或获取一个值,该值指示源列是否可以为 null。 这使得 System.Data.Common.DbCommandBuilder 能够正确地为可以为 + null 的列生成 Update 语句。 + + 返回结果: + 如果源列可以为 null,则为 true;如果不可以为 null,则为 false。 + + + + + 摘要: + 获取或设置一个值,该值指示参数是否接受空值。 + + 返回结果: + 如果接受 null 值,则为 true;否则为 false。 默认值为 false。 + + + + + 摘要: + 获取或设置在加载 System.Data.Common.DbParameter.Value 时使用的 System.Data.DataRowVersion。 + + 返回结果: + System.Data.DataRowVersion 值之一。 默认值为 Current。 + + 异常: + System.ArgumentException: + 该属性未设置为 System.Data.DataRowVersion 值之一。 + + + + + 摘要: + 将 DbType 属性重置为其原始设置。 + 此版本驱动不支持此功能 + + + + + XGParameters枚举器类。 + + + + + 枚举器中的当前元素。 + + + + + 构造方法。 + + 包含XGParameters的List对象 + + + + 移动到枚举器中的下一个元素。 + + 成功移动到下一元素返回True,否则返回False + + + + 将枚举器的位置移动到集合的第一个元素之前。 + + + + + 获取当前枚举器 + + 当前的枚举器 + + + + XGParameters集合类。 + + + + + 集合所拥有的元素个数。 + + + + + 构造XGParameterCollection对象。 + + + + + 向集合中添加XGParameters对象。 + + 需要被添加的XGParameters对象 + 被添加的元素在集合中的位置 + + + + 向集合中添加XGParameters对象。 + + 需要被添加的XGParameters对象 + 被添加成功的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值为value的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值 + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数值所占大小为size的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + XGParameters对象的参数值所占字节数 + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数方向为direction的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + XGParameters对象的参数方向,其取值为:Input,Output,InputOutput或ReturnValue + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数值为value,参数方向为direction的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + XGParameters对象的参数值 + XGParameters对象的参数方向,其取值为:Input,Output,InputOutput或ReturnValue + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数值大小为size,参数值为value,参数方向为direction的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + 参数值所占字节大小 + XGParameters对象的参数值 + XGParameters对象的参数方向,其取值为:Input,Output,InputOutput或ReturnValue + + + + + 将一个Array中包含的多个XGParameters对象批量添加到集合中。 + + 包含多个XGParameters对象的Array对象 + + + + 向集合中添加一个参数名为parameterName,参数值为value的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值 + 当前的XGParameters对象 + + + + 从集合中移除所有的XGParameters对象。 + + + + + 在集合中索引为index处插入一个参数值为value的XGParameters对象。 + + 插入的位置 + 被插入的XGParameters对象的参数值 + + + + 从集合中移除索引为index的XGParameters对象。 + + 需要被移除的XGParameters对象的索引 + + + + 从集合中移除参数名为parameterName的XGParameters对象。 + + 参数名 + + + + 当前集合中是否包含参数值为value的XGParameters对象。 + + XGParameters对象的参数值 + 包含返回True,否则返回False + + + + 当前集合中是否包含参数名为parameterName的XGParameters对象。 + + XGParameters对象的参数名 + 包含返回True,否则返回False + + + + 获取枚举器。 + + 当前集合的枚举器 + + + + 获取参数值为value的XGParameters对象在集合中的索引。返回-1表示此集合中不存在参数值为value的XGParameters对象。 + + 所查找的XGParameters对象的参数值 + 参数值为value的XGParameters对象在集合中的索引 + + + + 获取参数名为parameterName的XGParameters对象在集合中的索引。返回-1表示此集合中不存在参数名为parameterName的XGParameters对象。 + + 所查找的XGParameters对象的参数名 + 参数名为parameterName的XGParameters对象在集合中的索引 + + + + 根据索引从集合中获取XGParameters对象。 + + 索引值,从零开始 + 该索引的元素,若需使用XGParameters需强转 + + + + 根据XGParameters参数名从集合中获取DbParameters对象。 + + XGParameters参数名 + 目标元素,若需使用XGParameters需强转 + + + + 修改集合中索引为index的元素。 + + 索引值,从零开始 + 新的XGParameters对象 + + + + 修改集合中参数名为parameterName的元素。 + + XGParameters对象的参数名 + 新的XGParameters对象 + + + + 从集合中移除指定的XGParameters对象。 + + 需要被移除的XGParameters对象 + + + diff --git a/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/framework/XuguClient.dll b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/framework/XuguClient.dll new file mode 100644 index 00000000..d6810e9a Binary files /dev/null and b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/framework/XuguClient.dll differ diff --git a/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/framework/XuguClient.xml b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/framework/XuguClient.xml new file mode 100644 index 00000000..45981201 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/Driver-3.3.3.202212271548-RELEASE/framework/XuguClient.xml @@ -0,0 +1,1713 @@ + + + + XuguClient + + + + + 摘要: + 表示要对数据源执行的 SQL 语句或存储过程。 + + + + + 摘要: + 是否在下面的查询语句使用服务器端游标 + 默认是不启用游标 + add 2016-02-04 + + + + + 摘要: + 获取 System.Data.Common.DbParameter 对象的集合。 有关参数的更多信息,请参见 配置参数和参数数据类型。 + 返回结果: + SQL 语句或存储过程的参数。 + + + + + 摘要: + 开启批量执行多行参数绑定标志 + !=0 时 启动多行绑定功能 + + + + + 摘要: + 获取或设置针对数据源运行的文本命令。 + 返回结果: + 要执行的文本命令。 默认值为空字符串 ("")。 + + + + + 摘要: + 获取或设置在终止执行命令的尝试并生成错误之前的等待时间。 + + 返回结果: + 等待命令执行的时间(以秒为单位)。 + + + + + 摘要: + 获取或设置此 XGCommand 使用的 XGConnection。 + 返回结果: + 与数据源的连接。 + + + + + 摘要: + 释放由XGCommand 使用的托管资源,还可以选择释放非托管资源。 + + + + + + 摘要: + 获取或设置将在其中执行此 System.Data.Common.DbCommand 对象的 System.Data.Common.DbTransaction。 + 返回结果: + 要在其中执行 .NET Framework 数据提供程序的 Command 对象的事务。 默认值为 null 引用(在 Visual Basic + 中为 Nothing)。 + + + + + 摘要: + 指示或指定如何解释 System.Data.Common.DbCommand.CommandText 属性。 + 返回结果: + System.Data.CommandType 值之一。 默认值为 Text。 + + + + + 摘要: + 获取或设置一个值,该值指示此命令对象在自定义界面控件中是否可见。 + 返回结果: + 如果该命令对象应在控件中可见,则为 true;否则为 false。 + + + + + 摘要: + 获取或设置命令结果在由 System.Data.Common.DbDataAdapter 的 Update 方法使用时如何应用于 System.Data.DataRow。 + + 返回结果: + System.Data.UpdateRowSource 值之一。 如果该命令不是自动生成的,则默认值为 Both。 否则,默认值为 None。 + + + + + 摘要: + 尝试取消 XGCommand 的执行。 + + + + + 摘要: + 在数据源上创建该命令的准备好的(或已编译的)版本。 + 该功能目前不可用 + + + + + 摘要: + 对连接对象执行 SQL 语句。 + 返回结果: + 受影响的行数。 + 异常: + System.InValidConnectionException: + 指定的连接无效或者尚未打开。 + System.commandtextException: + CommandText不能为空 + + + + + 摘要: + 针对 System.Data.Common.DbCommand.Connection 执行 System.Data.Common.DbCommand.CommandText,并返回 + XGDataReader。 + + 返回结果: + System.Data.Common.DbDataReader 对象。 + + + + + 摘要: + 针对 System.Data.Common.DbCommand.Connection 执行 System.Data.Common.DbCommand.CommandText,并使用 + System.Data.CommandBehavior 值之一返回 System.Data.Common.DbDataReader。 + + 参数: + behavior: + System.Data.CommandBehavior 值之一。 + + 返回结果: + XGDataReader 对象。 + + + + + 摘要: + 获取或设置将在其中执行此 System.Data.Common.DbCommand 对象的 System.Data.Common.DbCommand.DbTransaction。 + 返回结果: + 要在其中执行 .NET Framework 数据提供程序的 Command 对象的事务。 默认值为 null 引用(在 Visual Basic + 中为 Nothing)。 + + + + + 摘要: + 获取或设置此 System.Data.Common.DbCommand 使用的 System.Data.Common.DbConnection。 + 返回结果: + 与数据源的连接。 + + + + + 摘要: + 获取 System.Data.Common.DbParameter 对象的集合。 + 返回结果: + SQL 语句或存储过程的参数。 + + + + + 摘要: + 创建 System.Data.Common.DbParameter 对象的新实例。 + 返回结果: + System.Data.Common.DbParameter 对象。 + + + + + 摘要: + 对连接执行命令文本。 + 参数: + behavior: + System.Data.CommandBehavior 的一个实例。 + 返回结果: + 表示该操作的任务。 + 异常: + System.Data.Common.DbException: + 执行命令文本时出现的一个错误。 + System.ArgumentException: + System.Data.CommandBehavior 值无效。 + + + + + 摘要: + 获取或设置用于打开连接的字符串。 + 返回结果: + 用来建立初始连接的连接字符串。该连接字符串的确切内容取决于此连接的特定数据源。默认值为空字符串。 + + + + + 摘要: + 获取描述连接状态的字符串。 + + 返回结果: + 连接的状态。 返回的字符串的格式取决于您正在使用的连接的特定类型。 + + + + + 摘要: + 获取在建立连接时终止尝试并生成错误之前所等待的时间。 + 返回结果: + 等待连接打开的时间(以秒为单位)。 默认值是由您正在使用的连接的特定类型确定的。 + + + + + 摘要: + 在连接打开之后获取当前数据库的名称,或者在连接打开之前获取连接字符串中指定的数据库名。 + 返回结果: + 当前数据库的名称或连接打开后要使用的数据库的名称。 默认值为空字符串。 + + + + + 摘要: + 获取要连接的数据库服务器的名称。 + 返回结果: + 要连接的数据库服务器的名称。 默认值为空字符串。 + + + + + 摘要: + 获取表示对象所连接到的服务器的版本的字符串。 + 返回结果: + 数据库的版本。 返回的字符串的格式取决于您正在使用的连接的特定类型。 + 异常: + System.InValidConnectionException: + 连接尚未处于活动状态 + System.InvalidOperationException: + 当返回的任务尚未完成,且调用 Overload:System.Data.Common.DbConnection.OpenAsync 后连接并未打开时,将调用 + System.Data.Common.DbConnection.ServerVersion。 + + + + + 摘要: + 开始数据库事务。 + + 返回结果: + 表示新事务的对象。 + + + + + 摘要: + 以指定的隔离级别启动数据库事务。 + + 参数: + isolationLevel: + 指定事务的隔离级别。 + + 返回结果: + 表示新事务的对象。 + + + + + 摘要: + 为打开的连接更改当前数据库。 + + 参数: + databaseName: + 为要使用的连接指定数据库名称。 + + + + + 摘要: + 当事件状态更改时发生。 + + + + + 摘要: + 创建并返回与当前连接关联的 System.Data.Common.DbCommand 对象。 + + 返回结果: + System.Data.Common.DbCommand 对象。 + + + + + 摘要: + 返回此 System.Data.Common.DbConnection 的数据源的架构信息。 + 此版本驱动不支持此功能 + 返回结果: + 包含架构信息的 System.Data.DataTable。 + + + + + 摘要: + 使用表示架构名称的指定字符串返回此 System.Data.Common.DbConnection 的数据源的架构信息。 + + 参数: + collectionName: + 指定要返回的架构的名称。 + + 返回结果: + 包含架构信息的 System.Data.DataTable。 + + 异常: + System.ArgumentException: + collectionName 被指定为空。 + + + + + 摘要: + 使用表示架构名称的指定字符串以及表示限制值的指定字符串数组返回此 System.Data.Common.DbConnection 的数据源的架构信息。 + + 参数: + collectionName: + 指定要返回的架构的名称。 + + restrictionValues: + 为请求的架构指定一组限制值。 + + 返回结果: + 包含架构信息的 System.Data.DataTable。 + + 异常: + System.ArgumentException: + collectionName 被指定为空。 + + + + + 摘要: + 使用 System.Data.Common.DbConnection.ConnectionString 所指定的设置打开数据库连接。 + + + + + 摘要: + 关闭与数据库的连接。 此方法是关闭任何已打开连接的首选方法。 + + 异常: + System.Data.Common.DbException: + 在打开连接时出现连接级别的错误。 + + + + + 摘要: + 释放资源 + + + + + 关闭服务器端游标 + + + + + 摘要: + 开始数据库事务。 + + 参数: + isolationLevel: + 指定事务的隔离级别。 + + 返回结果: + 表示新事务的对象。 + + + + + 摘要: + 获取此 System.Data.Common.DbConnection 的 System.Data.Common.DbProviderFactory。 + 返回结果: + 一组方法的集合,这些方法用于创建提供程序对数据源类的实现的实例。 + + + + + 摘要: + 创建并返回与当前连接关联的 System.Data.Common.DbCommand 对象。 + + 返回结果: + System.Data.Common.DbCommand 对象。 + + + + + 摘要: + 引发 System.Data.Common.DbConnection.StateChange 事件。 + + 参数: + stateChange: + 包含事件数据的 System.Data.StateChangeEventArgs。 + + + + + 如果连接字符串在设计器中可见,则为 true;否则为 false。默认值为 true。 + + + + + DbConnectionStringBuilder 是否具有固定大小。 + + + + + DbConnectionStringBuilder 是否为只读。 + + + + + 返回 一个 System.Collections.ICollection 内部包含System.Data.Common.DbConnectionStringBuilder 中的键。 + + + + + 摘要: + 获取或设置与指定的键关联的值。 + + 参数: + keyword: + 要获取或设置的项的键。 + + 返回结果: + 与指定的键相关联的值。 如果指定键未找到,尝试让其返回空引用(在 Visual Basic 中为 Nothing),并尝试设置为使用指定键创建新的元素。 + 传递 null 键(在 Visual Basic 中为 Nothing)将引发 System.ArgumentNullException。 分配一个删除键/值对的 + null 值。 + + 异常: + T:System.ArgumentNullException: + keyword 为空引用(在 Visual Basic 中为 Nothing)。 + + T:System.NotSupportedException: + 设置了该属性,但 System.Data.Common.DbConnectionStringBuilder 为只读。 - 或 - 设置了该属性,但 keyword + 不存在于集合中,且 System.Data.Common.DbConnectionStringBuilder 具有固定的大小。 + + + + + 摘要: + 将带有指定键和值的条目添加到 System.Data.Common.DbConnectionStringBuilder 中。 + + 参数: + keyword: + 要添加到 System.Data.Common.DbConnectionStringBuilder 的键。 + + value: + 指定键的值。 + + 异常: + T:System.ArgumentNullException: + keyword 为空引用(在 Visual Basic 中为 Nothing)。 + + T:System.NotSupportedException: + System.Data.Common.DbConnectionStringBuilder 为只读。 - 或 - System.Data.Common.DbConnectionStringBuilder + 具有固定的大小。 + + + + + 摘要: + 清除 System.Data.Common.DbConnectionStringBuilder 实例的内容。 + + 异常: + T:System.NotSupportedException: + System.Data.Common.DbConnectionStringBuilder 为只读。 + + + + + 摘要: + 清除关联的 System.Data.Common.DbConnectionStringBuilder 上的 System.ComponentModel.PropertyDescriptor + 对象集合。 + 清除关联的 PropertyDescriptor 上的 DbConnectionStringBuilder 对象集合。 + + 注解 + 设计器使用此方法清除 DbConnectionStringBuilder上的所有关联属性说明符。 + + + + + 摘要: + 确定 XGConnectionStringBuilder 是否包含特定键。 + + 参数: + keyword: + 要在 XGConnectionStringBuilder 中定位的键。 + + 返回结果: + 如果 XGConnectionStringBuilder 包含具有指定键的项,则为 true;否则为 false。 + + 异常: + T:System.ArgumentNullException: + keyword 为空引用(在 Visual Basic 中为 Nothing)。 + + + + + 摘要: + 将此 System.Data.Common.DbConnectionStringBuilder 对象中的连接信息与提供的对象中的连接信息进行比较。 + + 参数: + connectionStringBuilder: + 要与此 System.Data.Common.DbConnectionStringBuilder 对象进行比较的 System.Data.Common.DbConnectionStringBuilder。 + + 返回结果: + 如果两个 System.Data.Common.DbConnectionStringBuilder 对象中的连接信息生成等效的连接字符串,则为 true;否则为 + false。 + + + + + 摘要: + 使用有关此 Hashtable 的所有属性的信息,填充提供的 DbConnectionStringBuilder。 + + 参数 + propertyDescriptors + Hashtable + 使用有关此 Hashtable 的信息填充的 DbConnectionStringBuilder。 + + + + + 摘要: + 从 System.Data.Common.DbConnectionStringBuilder 实例中移除具有指定的键的项。 + + 参数: + keyword: + 要从此 System.Data.Common.DbConnectionStringBuilder 中的连接字符串移除的键/值对中的键。 + + 返回结果: + 如果该键存在于连接字符串中并已被移除,则为 true;如果该键不存在,则为 false。 + + 异常: + T:System.ArgumentNullException: + keyword 为 Null(在 Visual Basic 中为 Nothing) + + T:System.NotSupportedException: + System.Data.Common.DbConnectionStringBuilder 是只读,或 System.Data.Common.DbConnectionStringBuilder + 具有固定的大小。 + + + + + 摘要: + 指示此 XGConnectionStringBuilder 实例中是否存在指定的键。 + 参数 + keyword + String + 要在 XGConnectionStringBuilder 中定位的键。 + 返回 + Boolean + 如果 true 包含具有指定键的项,则为 XGConnectionStringBuilder;否则为 false。 + 注解 + 此方法的行为与 ContainsKey 方法完全相同。 + + + + + 摘要: + 返回与此 System.Data.Common.DbConnectionStringBuilder 关联的连接字符串。 + + 返回结果: + 当前 System.Data.Common.DbConnectionStringBuilder.ConnectionString 属性。 + + + + + 摘要: + 从此 System.Data.Common.DbConnectionStringBuilder 中检索与提供的键相对应的值。 + + 参数: + keyword: + 要检索的项的键。 + + value: + 与 key 对应的值。 + + 返回结果: + 如果在连接字符串中找到 keyword,则为 true,否则为 false。 + + 异常: + T:System.ArgumentNullException: + keyword 包含一个 null 值(在 Visual Basic 中为 Nothing)。 + + + + + 摘要: + 用一个 SQL SELECT 语句和一个 XuguCsConnecion初始化 XGDataAdapter 类的新实例。 + selectCommandText: 一个字符串,表示将由 XGDataAdapter 的 XGDataAdapter.SelectCommand 属性使用的 SQL SELECT 语句或存储过程。 + selectConnection: 表示连接的 XGConnection。 + + + + + 摘要: + 使用 SQL SELECT 语句和连接字符串初始化 XGDataAdapter类的新实例。 + selectCommandText: 一个字符串,表示将由 XGDataAdapter 的 XGDataAdapter.SelectCommand 属性使用的 SQL SELECT 语句或存储过程。 + selectConnectionString: 连接字符串。 + + + + + 从查询SQL中获取完整表名(模式名.表名) + + 查询SQL + 表名 或 模式名.表名 + + + + 摘要: + 获取一个值,该值指示当前行的嵌套深度。 + + 返回结果: + 当前行的嵌套深度。 + + + + + 摘要: + 获取当前行中的列数。 + + 返回结果: + 当前行中的列数。 + + 异常: + System.NotSupportedException: + 没有对 SQL Server 实例的当前连接。 + + + + + 摘要: + 获取一个值,它指示此 XGDataReader是否包含一个或多个行。 + + 返回结果: + 如果 XGDataReader 包含一行或多行,则为 true;否则为 false。 + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 获取一个值,该值指示 XGDataReader 是否已关闭。 + + 返回结果: + 如果 XGDataReader 已关闭,则为 true;否则为 false。 + + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 通过执行 SQL 语句获取更改、插入或删除的行数。 + + 返回结果: + 更改、插入或删除的行数。 对于 SELECT 语句为 -1;如果没影响任何行或该语句失败,则为 0。 + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 获取指定列的作为 System.Object 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.IndexOutOfRangeException: + 传递的索引位于 0 至 System.Data.IDataRecord.FieldCount 的范围之外。 + + + + + 摘要: + 获取指定列的作为 System.Object 的实例的值。 + + 参数: + name: + 列的名称。 + + 返回结果: + 指定列的值。 + + 异常: + System.IndexOutOfRangeException: + 未找到具有指定名称的列。 + + + + + 摘要: + 关闭 XGDataReader 对象。 + + + + + 摘要: + 释放由 XGDataReader 类的当前实例占用的所有资源。 + + + + + 摘要: + 获取指定列的布尔值形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的字节形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 从指定列读取一个字节流(从 dataOffset 指示的位置开始),读到缓冲区中(从 bufferOffset 指示的位置开始)。 + + 参数: + ordinal: + 从零开始的列序号。 + + dataOffset: + 行中的索引,从其开始读取操作。 + + buffer: + 作为数据复制目标的缓冲区。 + + bufferOffset: + 具有作为数据复制目标的缓冲区的索引。 + + length: + 最多读取的字符数。 + + 返回结果: + 读取的实际字节数。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的单个字符串形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 从指定列读取一个字符流,从 dataOffset 指示的位置开始,读到缓冲区中,从 bufferOffset 指示的位置开始。 + + 参数: + ordinal: + 从零开始的列序号。 + + dataOffset: + 行中的索引,从其开始读取操作。 + + buffer: + 作为数据复制目标的缓冲区。 + + bufferOffset: + 具有作为数据复制目标的缓冲区的索引。 + + length: + 最多读取的字符数。 + + 返回结果: + 读取的实际字符数。 + + + + + 摘要: + 获取指定列的数据类型的名称。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 表示数据类型的名称的字符串。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 返回被请求的列序号的 System.Data.Common.DbDataReader 对象。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + System.Data.Common.DbDataReader 对象。 + + + + + 摘要: + 获取指定列的 System.DateTime 对象形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 System.Decimal 对象形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的双精度浮点数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 返回一个 System.Collections.IEnumerator,可用于循环访问数据读取器中的行。 + + 返回结果: + 一个 System.Collections.IEnumerator,可用于循环访问数据读取器中的行。 + + + + + 摘要: + 获取指定列的数据类型。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的数据类型。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的单精度浮点数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的全局唯一标识符 (GUID) 形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 16 位有符号整数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 32 位有符号整数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的 64 位有符号整数形式的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + System.IndexOutOfRangeException: + 指定的列序号i无效 + + + + + 摘要: + 给定了从零开始的列序号时,获取列的名称。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的名称。 + + + + + 摘要: + 获取表的名称。 + 返回结果: + 表的名称。 + + + + + 摘要: + 给定列名称时,获取列序号。 + + 参数: + name: + 列的名称。 + + 返回结果: + 从零开始的列序号。 + + 异常: + System.IndexOutOfRangeException: + 指定的名称不是有效的列名称。 + + + + + 摘要: + 返回一个 System.Data.DataTable,它描述 System.Data.Common.DbDataReader 的列元数据。 + + 返回结果: + 一个描述列元数据的 System.Data.DataTable。 + + 异常: + System.InvalidOperationException: + XGDataReader 是关闭的。 + + + + + 摘要: + 获取指定列的作为 System.String 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的作为 XGClob 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的作为 XGBlob 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + 异常: + System.InvalidCastException: + 指定的强制转换无效。 + + + + + 摘要: + 获取指定列的作为 System.Object 的实例的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 指定列的值。 + + + + + 摘要: + 使用当前行的列值来填充对象数组。 + + 参数: + values: + 要将特性列复制到其中的 System.Object 数组。 + + 返回结果: + 数组中 System.Object 的实例的数目。 + + + + + 摘要: + 获取一个值,该值指示列中是否包含不存在的或已丢失的值。 + + 参数: + ordinal: + 从零开始的列序号。 + + 返回结果: + 如果指定的列与 System.DBNull 等效,则为 true;否则为 false。 + + + + + 摘要: + 读取批处理语句的结果时,使读取器前进到下一个结果。 + + 返回结果: + 如果存在多个结果集,则为 true;否则为 false。 + + + + + 摘要: + 将读取器XGDataReader 前进到结果集中的下一个记录。 + + 返回结果: + 如果存在多个行,则为 true;否则为 false。 + + + + + 摘要: + 释放由 XGDataReader 使用的托管资源,还可以选择释放非托管资源。 + + 参数: + disposing: + 若要释放托管资源和非托管资源,则为 true;若要仅释放非托管资源,则为 false。 + + + + + 无任何值(DBTYPE_EMPTY)。 + + + + + 布尔类型,其取值为:true、false或unknown。 + + + + + 固定长度字符串(SQL_CHAR)。它映射到 System.String。 + + + + + 8 位带符号的整数(DBTYPE_I1)。它映射到 System.SByte。 + + + + + 确切数值,其精度为 5,小数位数为 0(有符号:-32,768 <= n <= 32,767,没有符号:0 <= n <= 65,535)(SQL_SMALLINT)。它映射到 System.Int16。 + + + + + 确切数值,其精度为 10 和小数位数 0(有符号:-2[31] <= n <= 2[31] - 1,没有符号:0 <= n <= 2[32] - 1)(SQL_INTEGER)。它映射到 System.Int32。 + + + + + 确切数值,其精度为 19(如果有符号)或 20(如果没有符号),小数位数为 0(有符号:-2[63] <= n <= 2[63] - 1,没有符号:0 <= n <= 2[64] - 1)(SQL_BIGINT)。它映射到 System.Int64。 + + + + + 有符号的近似数值,其二进制精度为 24(零或绝对值为 10[-38] 到 10[38])(SQL_REAL)。它映射到 System.Single。 + + + + + 有符号的确切数值,其精度为 p,小数位数为 s,其中 1 <= p <= 15 并且 s <= p (SQL_NUMERIC)。该数据类型将映射到System.Decimal。 + + + + + 有符号的近似数值,其二进制精度为 53(零或绝对值为 10[-308] 到 10[308])(SQL_DOUBLE)。它映射到 System.Double。 + + + + + 日期数据,存储为双精度型(DBTYPE_DATE)。整数部分是自 1899 年 12 月 30 日以来的天数,而小数部分是不足一天的部分。它映射到System.DateTime。 + + + + + 格式为 yyyymmdd 的日期数据(SQL_TYPE_DATE)。它映射到 System.DateTime。 + + + + + 格式为 hhmmss 的日期数据(SQL_TYPE_TIMES)。它映射到 System.DateTime。 + + + + + 变长流字符串(SQL_CHAR)。它映射到 System.String。 + + + + + 二进制数据流 (SQL_BINARY)。它映射到 System.Byte 类型的 System.Array。 + + + + + 变长二进制。由用户设置该最大值 (SQL_VARBINARY)。它映射到 System.Byte 类型的 System.Array。 + + + + + 游标。可作为存储过程的输出参数类型和存储函数的返回值类型。 + + + + + 最大长度因数据源而定 (SQL_LONGVARCHAR)。它映射到 System.String。 + + + + + 长的二进制值(BLOB)。它映射到 System.Byte 类型的 System.Array。 + + + + + 根据DbType获取对应的XGDbType。 + + DbType + XGDbType + + + + 根据XGDbType获取对应的DbType。 + + XGDbType + DbType + + + + 根据值的Type和指定的XGDbType共同决定以获取对应的XGDbType。 + + 值的Type + XGDbType + 对应的XGDbType + + + + 根据数组类型获取对应的XGDbType。 + + 数组的Type + 参数的Type + 对应的XGDbType + + + + 用于参数绑定的类。 + + + + + 摘要: + 获取或设置参数的 System.Data.DbType。 + + 返回结果: + System.Data.DbType 值之一。 默认值为 System.Data.DbType.String。 + + 异常: + System.ArgumentException: + 该属性未设置为有效的 System.Data.DbType。 + + + + + 摘要: + 获取或设置该参数的值。 + + 返回结果: + 一个 System.Object,它是该参数的值。 默认值为 null。 + + + + + 创建作为当前实例副本的新对象。 + + 作为此实例副本的新对象。 + + + + 摘要: + 获取或设置一个值,该值指示参数是只可输入、只可输出、双向还是存储过程返回值参数。 + + 返回结果: + System.Data.ParameterDirection 值之一。 默认值为 Input。 + + 异常: + System.ArgumentException: + 该属性未设置为有效的 System.Data.ParameterDirection 值之一。 + + + + + 摘要: + 获取或设置列中数据的最大大小(以字节为单位)。 + + 返回结果: + 列中数据的最大大小(以字节为单位)。 默认值是从参数值推导出的。 + + + + + 无参的XGParameters构造方法。 + + + + + 通过参数名和参数值构造XGParameters对象。 + + 参数的名 + 参数的值 + + + + 通过XGParameters对象构造新的XGParameters副本。 + + 源XGParameters对象 + + + + 通过参数名和虚谷数据库的类型构造XGParameters对象。 + + 参数的名 + 虚谷数据库的类型 + + + + 通过参数名、虚谷数据库的类型和参数大小构造XGParameters对象。 + + 参数的名 + 虚谷数据库的类型 + 参数值所占字节大小 + + + + 构造XGParameters对象。 + + 参数的名 + 虚谷数据库的类型 + 参数值所占字节大小 + 参数的方向:Input,Output,InputOutput或ReturnValue + 无意义 + 无意义 + 无意义 + 无意义 + 无意义 + 参数的值 + + + + 摘要: + 获取或设置 System.Data.Common.DbParameter 的名称。 + + 返回结果: + System.Data.Common.DbParameter 的名称。 默认值为空字符串 ("")。 + + + + + 摘要: + 获取或设置源列的名称,该源列映射到 System.Data.DataSet 并用于加载或返回 System.Data.Common.DbParameter.Value。 + + 返回结果: + 映射到 System.Data.DataSet 的源列的名称。 默认值为空字符串。 + + + + + 摘要: + 设置或获取一个值,该值指示源列是否可以为 null。 这使得 System.Data.Common.DbCommandBuilder 能够正确地为可以为 + null 的列生成 Update 语句。 + + 返回结果: + 如果源列可以为 null,则为 true;如果不可以为 null,则为 false。 + + + + + 摘要: + 获取或设置一个值,该值指示参数是否接受空值。 + + 返回结果: + 如果接受 null 值,则为 true;否则为 false。 默认值为 false。 + + + + + 摘要: + 获取或设置在加载 System.Data.Common.DbParameter.Value 时使用的 System.Data.DataRowVersion。 + + 返回结果: + System.Data.DataRowVersion 值之一。 默认值为 Current。 + + 异常: + System.ArgumentException: + 该属性未设置为 System.Data.DataRowVersion 值之一。 + + + + + 摘要: + 将 DbType 属性重置为其原始设置。 + 此版本驱动不支持此功能 + + + + + XGParameters枚举器类。 + + + + + 枚举器中的当前元素。 + + + + + 构造方法。 + + 包含XGParameters的List对象 + + + + 移动到枚举器中的下一个元素。 + + 成功移动到下一元素返回True,否则返回False + + + + 将枚举器的位置移动到集合的第一个元素之前。 + + + + + 获取当前枚举器 + + 当前的枚举器 + + + + XGParameters集合类。 + + + + + 集合所拥有的元素个数。 + + + + + 构造XGParameterCollection对象。 + + + + + 向集合中添加XGParameters对象。 + + 需要被添加的XGParameters对象 + 被添加的元素在集合中的位置 + + + + 向集合中添加XGParameters对象。 + + 需要被添加的XGParameters对象 + 被添加成功的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值为value的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值 + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数值所占大小为size的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + XGParameters对象的参数值所占字节数 + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数方向为direction的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + XGParameters对象的参数方向,其取值为:Input,Output,InputOutput或ReturnValue + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数值为value,参数方向为direction的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + XGParameters对象的参数值 + XGParameters对象的参数方向,其取值为:Input,Output,InputOutput或ReturnValue + 当前的XGParameters对象 + + + + 向集合中添加一个参数名为parameterName,参数值类型为type,参数值大小为size,参数值为value,参数方向为direction的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值在虚谷数据库中的类型 + 参数值所占字节大小 + XGParameters对象的参数值 + XGParameters对象的参数方向,其取值为:Input,Output,InputOutput或ReturnValue + + + + + 将一个Array中包含的多个XGParameters对象批量添加到集合中。 + + 包含多个XGParameters对象的Array对象 + + + + 向集合中添加一个参数名为parameterName,参数值为value的XGParameters对象。 + + XGParameters对象的参数名 + XGParameters对象的参数值 + 当前的XGParameters对象 + + + + 从集合中移除所有的XGParameters对象。 + + + + + 在集合中索引为index处插入一个参数值为value的XGParameters对象。 + + 插入的位置 + 被插入的XGParameters对象的参数值 + + + + 从集合中移除索引为index的XGParameters对象。 + + 需要被移除的XGParameters对象的索引 + + + + 从集合中移除参数名为parameterName的XGParameters对象。 + + 参数名 + + + + 当前集合中是否包含参数值为value的XGParameters对象。 + + XGParameters对象的参数值 + 包含返回True,否则返回False + + + + 当前集合中是否包含参数名为parameterName的XGParameters对象。 + + XGParameters对象的参数名 + 包含返回True,否则返回False + + + + 获取枚举器。 + + 当前集合的枚举器 + + + + 获取参数值为value的XGParameters对象在集合中的索引。返回-1表示此集合中不存在参数值为value的XGParameters对象。 + + 所查找的XGParameters对象的参数值 + 参数值为value的XGParameters对象在集合中的索引 + + + + 获取参数名为parameterName的XGParameters对象在集合中的索引。返回-1表示此集合中不存在参数名为parameterName的XGParameters对象。 + + 所查找的XGParameters对象的参数名 + 参数名为parameterName的XGParameters对象在集合中的索引 + + + + 根据索引从集合中获取XGParameters对象。 + + 索引值,从零开始 + 该索引的元素,若需使用XGParameters需强转 + + + + 根据XGParameters参数名从集合中获取DbParameters对象。 + + XGParameters参数名 + 目标元素,若需使用XGParameters需强转 + + + + 修改集合中索引为index的元素。 + + 索引值,从零开始 + 新的XGParameters对象 + + + + 修改集合中参数名为parameterName的元素。 + + XGParameters对象的参数名 + 新的XGParameters对象 + + + + 从集合中移除指定的XGParameters对象。 + + 需要被移除的XGParameters对象 + + + diff --git a/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguAdo.cs b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguAdo.cs new file mode 100644 index 00000000..f17aa94b --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguAdo.cs @@ -0,0 +1,107 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using FreeSql.Internal.ObjectPool; +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading; +using XuguClient; + +namespace FreeSql.Xugu +{ + class XuguAdo : FreeSql.Internal.CommonProvider.AdoProvider + { + public XuguAdo() : base(DataType.PostgreSQL, null, null) { } + public XuguAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func connectionFactory) : base(DataType.PostgreSQL, masterConnectionString, slaveConnectionStrings) + { + base._util = util; + if (connectionFactory != null) + { + MasterPool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.PostgreSQL, connectionFactory); + return; + } + if (!string.IsNullOrEmpty(masterConnectionString)) + MasterPool = new XuguConnectionPool(CoreStrings.S_MasterDatabase, masterConnectionString, null, null); + if (slaveConnectionStrings != null) + { + foreach (var slaveConnectionString in slaveConnectionStrings) + { + var slavePool = new XuguConnectionPool($"{CoreStrings.S_SlaveDatabase}{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables)); + SlavePools.Add(slavePool); + } + } + } + + public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn) + { + if (param == null) return "NULL"; + if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false || param is JToken || param is JObject || param is JArray)) + param = Utils.GetDataReaderValue(mapType, param); + + bool isdic; + if (param is bool || param is bool?) + return (bool)param ? "'t'" : "'f'"; + else if (param is string) + return string.Concat("'", param.ToString().Replace("'", "''"), "'"); + else if (param is char) + return string.Concat("'", param.ToString().Replace("'", "''").Replace('\0', ' '), "'"); + else if (param is Enum) + return ((Enum)param).ToInt64(); + else if (decimal.TryParse(string.Concat(param), out var trydec)) + return param; + else if (param is DateTime || param is DateTime?) + return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'"); + else if (param is TimeSpan || param is TimeSpan?) + return ((TimeSpan)param).Ticks / 10; + else if (param is byte[]) + return $"'\\x{CommonUtils.BytesSqlRaw(param as byte[])}'"; + else if (param is JToken || param is JObject || param is JArray) + return string.Concat("'", param.ToString().Replace("'", "''"), "'::jsonb"); + else if ((isdic = param is Dictionary) || + param is IEnumerable>) + { + var pgdics = isdic ? param as Dictionary : + param as IEnumerable>; + + var pghstore = new StringBuilder("'"); + var pairs = pgdics.ToArray(); + + for (var i = 0; i < pairs.Length; i++) + { + if (i != 0) pghstore.Append(","); + + pghstore.AppendFormat("\"{0}\"=>", pairs[i].Key.Replace("'", "''")); + + if (pairs[i].Value == null) + pghstore.Append("NULL"); + else + pghstore.AppendFormat("\"{0}\"", pairs[i].Value.Replace("'", "''")); + } + + return pghstore.Append("'::hstore"); + } + else if (param is IEnumerable) + return AddslashesIEnumerable(param, mapType, mapColumn); + + return string.Concat("'", param.ToString().Replace("'", "''"), "'"); + } + + public override DbCommand CreateCommand() + { + return new XGCommand(); + } + + public override void ReturnConnection(IObjectPool pool, Object conn, Exception ex) + { + var rawPool = pool as XuguConnectionPool; + if (rawPool != null) rawPool.Return(conn, ex); + else pool.Return(conn); + } + + public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj); + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguConnectionPool.cs b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguConnectionPool.cs new file mode 100644 index 00000000..e5d4ec11 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguConnectionPool.cs @@ -0,0 +1,239 @@ +using FreeSql.Internal.ObjectPool; +using System; +using System.Collections.Concurrent; +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace FreeSql.Xugu +{ + + class XuguConnectionPool : ObjectPool + { + + internal Action availableHandler; + internal Action unavailableHandler; + + public XuguConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) + { + this.availableHandler = availableHandler; + this.unavailableHandler = unavailableHandler; + var policy = new XuguConnectionPoolPolicy + { + _pool = this, + Name = name + }; + this.Policy = policy; + policy.ConnectionString = connectionString; + } + + public void Return(Object obj, Exception exception, bool isRecreate = false) + { + //if (exception != null && exception is NpgsqlException) + //{ + // if (obj.Value.Ping() == false) + // base.SetUnavailable(exception, obj.LastGetTimeCopy); + //} + base.Return(obj, isRecreate); + } + } + + class XuguConnectionPoolPolicy : IPolicy + { + + internal XuguConnectionPool _pool; + public string Name { get; set; } = $"Xugu XuguConnection {CoreStrings.S_ObjectPool}"; + public int PoolSize { get; set; } = 50; + public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10); + public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(20); + public int AsyncGetCapacity { get; set; } = 10000; + public bool IsThrowGetTimeoutException { get; set; } = true; + public bool IsAutoDisposeWithSystem { get; set; } = true; + public int CheckAvailableInterval { get; set; } = 2; + public int Weight { get; set; } = 1; + + static ConcurrentDictionary dicConnStrIncr = new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase); + private string _connectionString; + public string ConnectionString + { + get => _connectionString; + set + { + _connectionString = value ?? ""; + + var minPoolSize = 0; + var pattern = @"Min(imum)?\s*pool\s*size\s*=\s*(\d+)"; + var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase); + if (m.Success) + { + minPoolSize = int.Parse(m.Groups[2].Value); + _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase); + } + + pattern = @"Max(imum)?\s*pool\s*size\s*=\s*(\d+)"; + m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase); + if (m.Success == false || int.TryParse(m.Groups[2].Value, out var poolsize) == false || poolsize <= 0) poolsize = Math.Max(50, minPoolSize); + var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => Math.Min(5, oldval + 1)); + PoolSize = poolsize + connStrIncr; + _connectionString = m.Success ? + Regex.Replace(_connectionString, pattern, $"Maximum pool size={PoolSize}", RegexOptions.IgnoreCase) : + $"{_connectionString};Maximum pool size={PoolSize}"; + + pattern = @"Connection\s*LifeTime\s*=\s*(\d+)"; + m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase); + if (m.Success) + { + IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value)); + _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase); + } + + FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize); + } + } + + public bool OnCheckAvailable(Object obj) + { + if (obj.Value == null) return false; + if (obj.Value.State == ConnectionState.Closed) obj.Value.Open(); + return obj.Value.Ping(true); + } + + public DbConnection OnCreate() + { + var conn = new XuguClient.XGConnection(_connectionString); + return conn; + } + + public void OnDestroy(DbConnection obj) + { + try { if (obj.State != ConnectionState.Closed) obj.Close(); } catch { } + + //try { XuguClient.XGConnection.ClearPool(obj as XuguClient.XGConnection); } catch { } + obj.Dispose(); + } + + public void OnGet(Object obj) + { + + if (_pool.IsAvailable) + { + if (obj.Value == null) + { + _pool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy); + throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name)); + } + + if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) + { + + try + { + obj.Value.Open(); + } + catch (Exception ex) + { + if (_pool.SetUnavailable(ex, obj.LastGetTimeCopy) == true) + throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}"); + throw ex; + } + } + } + } + +#if net40 +#else + async public Task OnGetAsync(Object obj) + { + + if (_pool.IsAvailable) + { + if (obj.Value == null) + { + _pool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy); + throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name)); + } + + if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (await obj.Value.PingAsync()) == false) + { + + try + { + await obj.Value.OpenAsync(); + } + catch (Exception ex) + { + if (_pool.SetUnavailable(ex, obj.LastGetTimeCopy) == true) + throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}"); + throw ex; + } + } + } + } +#endif + + public void OnGetTimeout() + { + + } + + public void OnReturn(Object obj) + { + //if (obj?.Value != null && obj.Value.State != ConnectionState.Closed) try { obj.Value.Close(); } catch { } + } + + public void OnAvailable() + { + _pool.availableHandler?.Invoke(); + } + + public void OnUnavailable() + { + _pool.unavailableHandler?.Invoke(); + } + } + + static class DbConnectionExtensions + { + + static DbCommand PingCommand(DbConnection conn) + { + var cmd = conn.CreateCommand(); + cmd.CommandTimeout = 5; + cmd.CommandText = "select 1"; + return cmd; + } + public static bool Ping(this DbConnection that, bool isThrow = false) + { + try + { + PingCommand(that).ExecuteNonQuery(); + return true; + } + catch + { + if (that.State != ConnectionState.Closed) try { that.Close(); } catch { } + if (isThrow) throw; + return false; + } + } + +#if net40 +#else + async public static Task PingAsync(this DbConnection that, bool isThrow = false) + { + try + { + await PingCommand(that).ExecuteNonQueryAsync(); + return true; + } + catch + { + if (that.State != ConnectionState.Closed) try { that.Close(); } catch { } + if (isThrow) throw; + return false; + } + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguTypesConverter.cs b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguTypesConverter.cs new file mode 100644 index 00000000..9099bfac --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguTypesConverter.cs @@ -0,0 +1,155 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; + +namespace Newtonsoft.Json +{ + public class XuguTypesConverter : JsonConverter + { + private static readonly Type typeof_BitArray = typeof(BitArray); + + //private static readonly Type typeof_NpgsqlPoint = typeof(NpgsqlPoint); + //private static readonly Type typeof_NpgsqlLine = typeof(NpgsqlLine); + //private static readonly Type typeof_NpgsqlLSeg = typeof(NpgsqlLSeg); + //private static readonly Type typeof_NpgsqlBox = typeof(NpgsqlBox); + //private static readonly Type typeof_NpgsqlPath = typeof(NpgsqlPath); + //private static readonly Type typeof_NpgsqlPolygon = typeof(NpgsqlPolygon); + //private static readonly Type typeof_NpgsqlCircle = typeof(NpgsqlCircle); + + //private static readonly Type typeof_Cidr = typeof((IPAddress, int)); + //private static readonly Type typeof_IPAddress = typeof(IPAddress); + //private static readonly Type typeof_PhysicalAddress = typeof(PhysicalAddress); + + private static readonly Type typeof_String = typeof(string); + + //private static readonly Type typeof_NpgsqlRange_int = typeof(NpgsqlRange); + //private static readonly Type typeof_NpgsqlRange_long = typeof(NpgsqlRange); + //private static readonly Type typeof_NpgsqlRange_decimal = typeof(NpgsqlRange); + //private static readonly Type typeof_NpgsqlRange_DateTime = typeof(NpgsqlRange); + public override bool CanConvert(Type objectType) + { + Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType; + var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault(); + + if (ctype == typeof_BitArray) return true; + + //if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return true; + //if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return true; + //if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return true; + //if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return true; + //if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return true; + //if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return true; + //if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return true; + + //if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) return true; + //if (ctype == typeof_IPAddress) return true; + //if (ctype == typeof_PhysicalAddress) return true; + + //if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return true; + //if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return true; + //if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return true; + //if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return true; + + return false; + } + private object YieldJToken(Type ctype, JToken jt, int rank) + { + if (jt.Type == JTokenType.Null) return null; + if (rank == 0) + { + var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();//ctype.Namespace == "System" && ctype.Name.StartsWith("Nullable`") ? ctype.GenericTypeArguments.FirstOrDefault() : null; + if (ctype == typeof_BitArray) return jt.ToString().ToBitArray(); + + //if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return NpgsqlPoint.Parse(jt.ToString()); + //if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return NpgsqlLine.Parse(jt.ToString()); + //if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return NpgsqlLSeg.Parse(jt.ToString()); + //if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return NpgsqlBox.Parse(jt.ToString()); + //if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return NpgsqlPath.Parse(jt.ToString()); + //if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return NpgsqlPolygon.Parse(jt.ToString()); + //if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return NpgsqlCircle.Parse(jt.ToString()); + + //if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) + //{ + // var cidrArgs = jt.ToString().Split(new[] { '/' }, 2); + // return (IPAddress.Parse(cidrArgs.First()), cidrArgs.Length >= 2 ? int.TryParse(cidrArgs[1], out var tryCdirSubnet) ? tryCdirSubnet : 0 : 0); + //} + //if (ctype == typeof_IPAddress) return IPAddress.Parse(jt.ToString()); + //if (ctype == typeof_PhysicalAddress) return PhysicalAddress.Parse(jt.ToString()); + + //if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return jt.ToString().ToNpgsqlRange(); + //if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return jt.ToString().ToNpgsqlRange(); + //if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return jt.ToString().ToNpgsqlRange(); + //if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return jt.ToString().ToNpgsqlRange(); + + return null; + } + + var jtarr = jt.ToArray(); + var ret = Array.CreateInstance(ctype, jtarr.Length); + var jtarrIdx = 0; + foreach (var a in jtarr) + { + var t2 = YieldJToken(ctype, a, rank - 1); + ret.SetValue(t2, jtarrIdx++); + } + return ret; + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + int rank = objectType.IsArray ? objectType.GetArrayRank() : 0; + Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType; + + var ret = YieldJToken(ctype, JToken.Load(reader), rank); + if (ret != null && rank > 0) return ret; + return ret; + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Type objectType = value.GetType(); + if (objectType.IsArray) + { + int rank = objectType.GetArrayRank(); + int[] indices = new int[rank]; + GetJObject(value as Array, indices, 0).WriteTo(writer); + } + else + GetJObject(value).WriteTo(writer); + } + public static JToken GetJObject(object value) + { + if (value is BitArray) return JToken.FromObject((value as BitArray)?.To1010()); + if (value is IPAddress) return JToken.FromObject((value as IPAddress)?.ToString()); + if (value is ValueTuple || value is ValueTuple?) + { + ValueTuple? cidrValue = (ValueTuple?)value; + return JToken.FromObject(cidrValue == null ? null : $"{cidrValue.Value.Item1.ToString()}/{cidrValue.Value.Item2.ToString()}"); + } + return JToken.FromObject(value?.ToString()); + } + public static JToken GetJObject(Array value, int[] indices, int idx) + { + if (idx == indices.Length) + { + return GetJObject(value.GetValue(indices)); + } + JArray ja = new JArray(); + if (indices.Length == 1) + { + foreach (object a in value) + ja.Add(GetJObject(a)); + return ja; + } + int lb = value.GetLowerBound(idx); + int ub = value.GetUpperBound(idx); + for (int b = lb; b <= ub; b++) + { + indices[idx] = b; + ja.Add(GetJObject(value, indices, idx + 1)); + } + return ja; + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguTypesExtensions.cs b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguTypesExtensions.cs new file mode 100644 index 00000000..1f7106a1 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguAdo/XuguTypesExtensions.cs @@ -0,0 +1,25 @@ +using System.Collections; + +public static partial class XuguTypesExtensions +{ + + public static string To1010(this BitArray ba) + { + char[] ret = new char[ba.Length]; + for (int a = 0; a < ba.Length; a++) ret[a] = ba[a] ? '1' : '0'; + return new string(ret); + } + + /// + /// 将 1010101010 这样的二进制字符串转换成 BitArray + /// + /// 1010101010 + /// + public static BitArray ToBitArray(this string _1010Str) + { + if (_1010Str == null) return null; + BitArray ret = new BitArray(_1010Str.Length); + for (int a = 0; a < _1010Str.Length; a++) ret[a] = _1010Str[a] == '1'; + return ret; + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Xugu/XuguCodeFirst.cs b/Providers/FreeSql.Provider.Xugu/XuguCodeFirst.cs new file mode 100644 index 00000000..06641b8e --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguCodeFirst.cs @@ -0,0 +1,511 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Numerics; +using System.Text; +using System.Text.RegularExpressions; +using XuguClient; + +namespace FreeSql.Xugu +{ + + class XuguCodeFirst : Internal.CommonProvider.CodeFirstProvider + { + public readonly string DefaultSchema = "SYSDBA";//默认模式 + public XuguCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { + } + + static object _dicCsToDbLock = new object(); + static Dictionary> _dicCsToDb = new Dictionary>() { + + { + typeof(byte).FullName, + CsToDb.New(XGDbType.SmallInt, "TINYINT","TINYINT NOT NULL", false, false, 0) + }, + + { typeof(byte?).FullName, CsToDb.New(XGDbType.SmallInt, "TINYINT", "TINYINT", false, true, null) }, + { typeof(short).FullName, CsToDb.New(XGDbType.SmallInt, "SMALLINT","SMALLINT NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(XGDbType.SmallInt, "SMALLINT", "SMALLINT", false, true, null) }, + { typeof(int).FullName, CsToDb.New(XGDbType.Int, "INTEGER","INTEGER NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(XGDbType.Int, "INTEGER", "INTEGER", false, true, null) }, + { typeof(long).FullName, CsToDb.New(XGDbType.BigInt, "BIGINT","BIGINT NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(XGDbType.BigInt, "BIGINT", "BIGINT", false, true, null) }, + + + { typeof(ushort).FullName, CsToDb.New(XGDbType.Int, "INT","INT NOT NULL", false, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(XGDbType.Int, "INT", "INT", false, true, null) }, + { typeof(uint).FullName, CsToDb.New(XGDbType.BigInt, "BIGINT","BIGINT NOT NULL", false, false, 0) },{ typeof(uint?).FullName, CsToDb.New(XGDbType.BigInt, "BIGINT", "BIGINT", false, true, null) }, + { typeof(ulong).FullName, CsToDb.New(XGDbType.Numeric, "NUMERIC","NUMERIC(20,0) NOT NULL", false, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(XGDbType.Numeric, "NUMERIC", "NUMERIC(20,0)", false, true, null) }, + + { typeof(float).FullName, CsToDb.New(XGDbType.Real, "FLOAT","FLOAT NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(XGDbType.Real, "FLOAT", "FLOAT", false, true, null) }, + { typeof(double).FullName, CsToDb.New(XGDbType.Double, "DOUBLE","DOUBLE NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(XGDbType.Double, "DOUBLE", "DOUBLE", false, true, null) }, + { typeof(decimal).FullName, CsToDb.New(XGDbType.Numeric, "NUMERIC", "NUMERIC(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(XGDbType.Numeric, "NUMERIC", "NUMERIC(10,2)", false, true, null) }, + + { typeof(string).FullName, CsToDb.New(XGDbType.VarChar, "VARCHAR", "VARCHAR(255)", false, null, "") }, + + { typeof(char).FullName, CsToDb.New(XGDbType.Char, "CHAR", "CHAR(1)", false, null, '\0') }, + + //{ typeof(TimeSpan).FullName, CsToDb.New(XGDbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(XGDbType.Time, "time", "time",false, true, null) }, + { typeof(DateTime).FullName, CsToDb.New(XGDbType.DateTime, "DATETIME", "DATETIME NOT NULL", false, false, new DateTime(1970,1,1)) }, + { typeof(DateTime?).FullName, CsToDb.New(XGDbType.DateTime, "DATETIME", "DATETIME", false, true, null) }, + + { typeof(bool).FullName, CsToDb.New(XGDbType.Bool, "BOOLEAN","BOOLEAN NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(XGDbType.Bool, "BOOLEAN","BOOLEAN", null, true, null) }, + + { typeof(byte[]).FullName, CsToDb.New(XGDbType.VarBinary, "blob", "blob NULL", false, null, new byte[0]) }, + { typeof(Guid).FullName, CsToDb.New(XGDbType.Char, "char", "char(36) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(XGDbType.Char, "char", "char(36) NULL", false, true, null) }, + + + + //{ typeof(Dictionary).FullName, CsToDb.New(XGDbType.Hstore, "hstore", "hstore", false, null, new Dictionary()) }, + + }; + + + public override DbInfoResult GetDbInfo(Type type) + { + _dicCsToDb.TryGetValue(type.FullName, out var info); + if (info == null) return null; + return new DbInfoResult((int)info.type, info.dbtype, info.dbtypeFull, info.isnullable, info.defaultValue); + } + internal static string GetXuguSqlTypeFullName(object[] row) + { + var a = row; + var sqlType = string.Concat(a[1]).ToUpper(); + var data_length = long.Parse(string.Concat(a[2])); + var char_used = string.Concat(a[5]); + bool.TryParse(a[9]?.ToString(), out var isVar); + if (sqlType == "CHAR" && isVar) + { + sqlType = "VARCHAR"; + } + + if (data_length <= 0) + { + } + else if (sqlType.StartsWith("TIMESTAMP", StringComparison.CurrentCultureIgnoreCase)) + { + } + else if (sqlType.StartsWith("DATETIME", StringComparison.CurrentCultureIgnoreCase)) + { + } + else if (sqlType.StartsWith("BLOB")) + { + } + else if (sqlType.StartsWith("CLOB")) + { + } + else if (sqlType.ToUpper()== "NUMERIC") + { + //data_length=655362 + //实际类型是 NUMRIC(10,2) 计算得到的是 NUMRIC(10,12) + //标度计算错误 + + var data_precision= data_length % 65536; + var data_scale = data_length / 65536; + sqlType += $"({data_scale},{data_precision})"; + } + else if (sqlType.ToLower() == "float") + { } + else + sqlType += $"({data_length})"; + return sqlType; + } + protected override string GetComparisonDDLStatements(params TypeAndName[] objects) + { + var sb = new StringBuilder(); + var seqcols = new List>(); //序列 + + foreach (var obj in objects) + { + if (sb.Length > 0) sb.Append("\r\n"); + var tb = _commonUtils.GetTableByEntity(obj.entityType); + if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName)); + if (tb.Columns.Any() == false) throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName)); + var tbname = _commonUtils.SplitTableName(tb.DbName); + if (tbname?.Length == 1) tbname = new[] { DefaultSchema, tbname[0] }; + + var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名 + + if (tboldname?.Length == 1) tboldname = new[] { DefaultSchema, tboldname[0] }; + if (string.IsNullOrEmpty(obj.tableName) == false) + { + var tbtmpname = _commonUtils.SplitTableName(obj.tableName); + if (tbtmpname?.Length == 1) tbtmpname = new[] { DefaultSchema, tbtmpname[0] }; + if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1]) + { + tbname = tbtmpname; + tboldname = null; + } + } + //codefirst 不支持表名、模式名、数据库名中带 . + + if (_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_schemas where schema_name={0}", tbname[0])) == null) //创建模式 + throw new Exception($"模式“{tbname[0]}”不存在,请手动创建"); + //sb.Append($"CREATE SCHEMA {tbname[0]};\r\n"); + //sb.Append("CREATE SCHEMA IF NOT EXISTS ").Append(tbname[0]).Append(";\r\n"); + + var sbalter = new StringBuilder(); + var istmpatler = false; //创建临时表,导入数据,删除旧表,修改 + + //虚谷 + var sql0 = string.Format("select 1 from all_tables a inner join all_schemas b on b.schema_id = a.schema_id where b.schema_name || '.' || a.table_name = '{0}.{1}'", tbname); + + + //判断表是否存在 + if (_orm.Ado.ExecuteScalar(CommandType.Text, sql0) == null) + { + //表不存在 + if (tboldname != null) + { + if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from all_tables a inner join all_schemas b on b.schema_id = a.schema_id where b.schema_name || '.' || a.table_name = '{0}.{1}'", tboldname)) == null) + //旧表不存在 + tboldname = null; + } + if (tboldname == null) + { + //创建表 + var createTableName = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}"); + sb.Append("CREATE TABLE ").Append(createTableName).Append(" ( "); + foreach (var tbcol in tb.ColumnsByPosition) + { + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" "); + if (tbcol.Attribute.IsIdentity == true) + { + sb.Append(tbcol.Attribute.DbType.Replace("NOT NULL","")); + sb.Append(" identity(1,1)"); + //seqcols.Add(NativeTuple.Create(tbcol, tbname, true)); + } + else + { + sb.Append(tbcol.Attribute.DbType.Replace("NTEXT", "CLOB").Replace("TEXT", "CLOB")); + } + sb.Append(","); + } + if (tb.Primarys.Any()) + { + var pkname = $"{tbname[0]}_{tbname[1]}_pkey"; + sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(pkname)).Append(" 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"); + + //创建表的索引 + foreach (var uk in tb.Indexes) + { + sb.Append("CREATE "); + if (uk.IsUnique) sb.Append("UNIQUE "); + sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(createTableName).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(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append(";\r\n"); + else + { + //如果新表,旧表不在一起,创建新表,导入数据,删除旧表 + istmpatler = true; + } + } + else + tboldname = null; //如果新表已经存在,不走改表名逻辑 + + + var sql = _commonUtils.FormatSql($@" +select +a.COL_NAME, +a.TYPE_NAME, +a.SCALE, +case when a.NOT_NULL then '0' else '1' end as is_nullable, +seq.SEQ_ID, +seq.IS_SYS, +a.COMMENTS, +tb.table_name, +ns.schema_id, +a.`VARYING` +from all_columns as a +left join all_tables tb on a.table_id=tb.table_id +left join all_schemas ns on tb.schema_id = ns.schema_id +left join all_sequences seq on seq.SEQ_ID=a.Serial_ID +WHERE ns.SCHEMA_NAME={{0}} and tb.TABLE_NAME={{1}} + +", tboldname ?? tbname); + var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + //从系统表读取的现有字段的基本信息 + var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => + { + var sqlType = GetXuguSqlTypeFullName(a); + var max_length = long.Parse(string.Concat(a[2])); + //long SEQ_ID = 0; + long.TryParse(a[4]?.ToString() ?? "0", out long SEQ_ID); + var SEQ_IS_SYS = (a[5]?.ToString() ?? "false")?.ToLower(); + try + { + return new + { + column = string.Concat(a[0]), + sqlType = string.Concat(sqlType), + max_length = long.Parse(string.Concat(a[2]) ?? "0"), + is_nullable = string.Concat(a[3]) == "1", + is_identity = (SEQ_ID > 0 && SEQ_IS_SYS == "true"), + comment = string.Concat(a[6]) + }; + } + catch (Exception ex) + { + + throw ex; + } + + }, StringComparer.CurrentCultureIgnoreCase); + + if (istmpatler == false) + { + //基本信息对比 比如名称 添加列 数据类型 Identity + foreach (var tbcol in tb.ColumnsByPosition) + { + //对别数据库中列和C#中的信息是否一致 + if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) || + string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) + { + var isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? ""); + var sqlTypeSize = tbstructcol.sqlType; + //如果数据中是CHAR或VARCHAR 要加上C#中定义的长度 + if (sqlTypeSize.Contains("(") == false) + { + switch (sqlTypeSize) + { + case "char": + case "varchar": + sqlTypeSize = $"{sqlTypeSize}({tbstructcol.max_length})"; break; + } + } + + var dbType = tbcol.Attribute.DbType.Replace("NTEXT", "CLOB").Replace("TEXT", "CLOB"); + if (dbType.StartsWith(sqlTypeSize, StringComparison.CurrentCultureIgnoreCase) == false) + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.DbType.Split(' ').First().Replace("NTEXT", "CLOB").Replace("TEXT", "CLOB")).Append(";\r\n");//为了适应FreeSQL的长文本规则采用Replace + + + + if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable) + { + if (tbcol.Attribute.IsNullable != true || tbcol.Attribute.IsNullable == true && tbcol.Attribute.IsPrimary == false) + { + if (tbcol.Attribute.IsNullable == false) + sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" = ").Append(tbcol.DbDefaultValue).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" IS NULL;\r\n"); + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable == true ? "DROP" : "SET").Append(" NOT NULL;\r\n"); + } + } + if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity) + { + //sbalter.Append(" identity(1,1)"); + seqcols.Add(NativeTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true)); + } + if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0) + //修改列名 + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" RENAME COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(";\r\n"); + + if (isCommentChanged) + sbalter.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + + continue; + } + //添加列 + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n"); + sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" = ").Append(tbcol.DbDefaultValue).Append(";\r\n"); + if (tbcol.Attribute.IsNullable == false) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" SET NOT NULL;\r\n"); + if (tbcol.Attribute.IsIdentity == true) + { + seqcols.Add(NativeTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true)); + } + if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + } + + var dsuksql = _commonUtils.FormatSql($@" +select +keys, +a.index_name, +case when a.IS_UNIQUE then 1 else 0 end as IsUnique, +t.table_name, +ns.SCHEMA_NAME, +a.IS_SYS +from all_INDEXES a +left join all_tables t on a.TABLE_ID=t.TABLE_ID +left join all_schemas ns on t.SCHEMA_ID=ns.SCHEMA_ID +WHERE a.IS_Primary = false and t.table_name={{1}} and schema_name={{0}}", + tboldname ?? tbname); + + var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]), string.Concat(a[2]), string.Concat(a[3]), string.Concat(a[4]) }).ToList(); + foreach (var uk in tb.Indexes) + { + if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) continue; + //获取C#中定义的索引名称 + var ukname = ReplaceIndexName(uk.Name, tbname[1]); + //数据库和C#定义名称一致的 + var dsukfind1 = dsuk.FirstOrDefault(a => string.Compare(a[1], ukname, true) == 0); + + if (dsukfind1==null || //没有找到一致的索引,表示新定义的 + dsukfind1[0].Split(',').Length!=uk.Columns.Length || + uk.Columns.Any(x=>dsukfind1[0].ToLower().IndexOf( x.Column.Attribute.Name.ToLower())==-1) || + uk.IsUnique != (dsukfind1[2]== "1") + ) + { + if (dsukfind1 != null) sbalter.Append("DROP INDEX ").Append($"\"{dsukfind1[4]}\".\"{dsukfind1[3]}\".\"{ukname}\"").Append(";\r\n"); + sbalter.Append("CREATE "); + if (uk.IsUnique) sbalter.Append("UNIQUE "); + sbalter.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append("("); + foreach (var tbcol in uk.Columns) + { + sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); + //if (tbcol.IsDesc) sbalter.Append(" DESC"); + sbalter.Append(", "); + } + sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n"); + } + } + } + if (istmpatler == false) + { + //描述 + var dbcomment = string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql($@" +select COMMENTS,TABLE_NAME,SCHEMA_NAME from all_tables as a +left join all_schemas as b on a.SCHEMA_ID=b.SCHEMA_ID +WHERE a.Table_Name={{1}} and b.SCHEMA_NAME={{0}} +",tbname))); + if ((dbcomment??"") != (tb.Comment ?? "")) + sbalter.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\r\n"); + + sb.Append(sbalter); + continue; + } + + //约束primary key + + var oldpk = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@" +select cons_name from all_constraints as a +left join all_tables as b on a.Table_ID=b.Table_ID +left Join all_SCHEMAS AS c on b.SCHEMA_ID=c.SCHEMA_ID +where b.TABLE_NAME={0} and c.SCHEMA_NAME={1} and a.cons_TYPE='P' +", tbname))?.ToString(); + if (string.IsNullOrEmpty(oldpk) == false) + sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(oldpk).Append(";\r\n"); + + //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名 + var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}"); + + //创建临时表 + var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.FreeSqlTmp_{tbname[1]}"); + sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( "); + foreach (var tbcol in tb.ColumnsByPosition) + { + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType); + if (tbcol.Attribute.IsIdentity == true) + { + //sb.Append(" identity(1,1)"); + seqcols.Add(NativeTuple.Create(tbcol, tbname, true)); + } + sb.Append(","); + } + + + + if (tb.Primarys.Any()) + { + var pkname = $"{tbname[0]}_{tbname[1]}_pkey"; + sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(pkname)).Append(" 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"); + //备注 + foreach (var tbcol in tb.ColumnsByPosition) + { + if (string.IsNullOrEmpty(tbcol.Comment) == false) + sb.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FreeSqlTmp_{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + } + if (string.IsNullOrEmpty(tb.Comment) == false) + sb.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FreeSqlTmp_{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\r\n"); + + sb.Append("INSERT INTO ").Append(tmptablename).Append(" ("); + foreach (var tbcol in tb.ColumnsByPosition) + sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); + sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT "); + foreach (var tbcol in tb.ColumnsByPosition) + { + var insertvalue = "NULL"; + if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) || + string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) + { + insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column); + if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false) + insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})"; + if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable) + insertvalue = $"coalesce({insertvalue},{tbcol.DbDefaultValue})"; + } + else if (tbcol.Attribute.IsNullable == false) + insertvalue = tbcol.DbDefaultValue; + sb.Append(insertvalue).Append(", "); + } + sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n"); + sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n"); + sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[1])).Append(";\r\n"); + //创建表的索引 + foreach (var uk in tb.Indexes) + { + sb.Append("CREATE "); + if (uk.IsUnique) sb.Append("UNIQUE "); + sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(tablename).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 seqcol in seqcols) + //{ + // var tbname = seqcol.Item2; + // var seqname = 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("'::regclass);\r\n"); + // sb.Append(" SELECT case when max(").Append(colname2).Append(") is null then 0 else setval('").Append(seqname).Append("', max(").Append(colname2).Append(")) end FROM ").Append(tbname2).Append(";\r\n"); + // } + //} + Console.Write(sb.ToString()); + //throw new Exception(sb.ToString()); + return sb.Length == 0 ? null : sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Xugu/XuguDbFirst.cs b/Providers/FreeSql.Provider.Xugu/XuguDbFirst.cs new file mode 100644 index 00000000..b040d8fd --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguDbFirst.cs @@ -0,0 +1,543 @@ +using FreeSql.DatabaseModel; +using FreeSql.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlTypes; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Text; +using System.Text.RegularExpressions; +using XuguClient; + +namespace FreeSql.Xugu +{ + class XuguDbFirst : IDbFirst + { + public readonly string DefaultSchema = "SYSDBA";//默认模式 + IFreeSql _orm; + protected CommonUtils _commonUtils; + protected CommonExpression _commonExpression; + public XuguDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + { + _orm = orm; + _commonUtils = commonUtils; + _commonExpression = commonExpression; + } + + public bool IsPg10 => ServerVersion >= 10; + public int ServerVersion + { + get + { + if (_ServerVersionValue == 0 && _orm.Ado.MasterPool != null) + using (var conn = _orm.Ado.MasterPool.Get()) + { + try + { + _ServerVersionValue = int.Parse(conn.Value.ServerVersion.Split('.')[0]); + } + catch + { + _ServerVersionValue = 9; + } + } + return _ServerVersionValue; + } + } + int _ServerVersionValue = 0; + + public int GetDbType(DbColumnInfo column) => (int)GetXGDbType(column); + XGDbType GetXGDbType(DbColumnInfo column) + { + var dbtype = column.DbTypeText; + var isarray = dbtype?.EndsWith("[]") == true; + if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2); + XGDbType ret = XGDbType.LongVarChar; + switch (dbtype?.ToUpper().TrimStart('_')) + { + case "SMALLINT": ret = XGDbType.SmallInt; break; + case "INTEGER": ret = XGDbType.Int; break; + case "BIGINT": ret = XGDbType.BigInt; break; + case "NUMERIC": ret = XGDbType.Numeric; break; + case "FLOAT": ret = XGDbType.Real; break; + case "DOUBLE": ret = XGDbType.Double; break; + //case "money": ret = XGDbType.; break; + + case "CHAR": ret = XGDbType.Char; break; + case "VARCHAR": ret = XGDbType.VarChar; break; + case "CLOB": ret = XGDbType.LongVarChar; break; + + //case "timestamp": ret = XGDbType.DateTime; break; + //case "timestamptz": ret = XGDbType.DateTime; break; + //case "date": ret = XGDbType.Date; break; + //case "time": ret = XGDbType.Time; break; + //case "timetz": ret = XGDbType.TimeTz; break; + //case "interval": ret = XGDbType.Interval; break; + + case "BOOLEAN": ret = XGDbType.Bool; break; + //case "bytea": ret = XGDbType.Bytea; break; + //case "bit": ret = XGDbType.Bool; break; + //case "varbit": ret = XGDbType.Varbit; break; + + //case "point": ret = XGDbType.Point; break; + //case "line": ret = XGDbType.Line; break; + //case "lseg": ret = XGDbType.LSeg; break; + //case "box": ret = XGDbType.Box; break; + //case "path": ret = XGDbType.Path; break; + //case "polygon": ret = XGDbType.Polygon; break; + //case "circle": ret = XGDbType.Circle; break; + + //case "cidr": ret = XGDbType.Cidr; break; + //case "inet": ret = XGDbType.Inet; break; + //case "macaddr": ret = XGDbType.MacAddr; break; + + //case "json": ret = XGDbType.Json; break; + //case "jsonb": ret = XGDbType.Jsonb; break; + //case "uuid": ret = XGDbType.Uuid; break; + + //case "int4range": ret = XGDbType.Range | XGDbType.Integer; break; + //case "int8range": ret = XGDbType.Range | XGDbType.Bigint; break; + //case "numrange": ret = XGDbType.Range | XGDbType.Numeric; break; + //case "tsrange": ret = XGDbType.Range | XGDbType.Timestamp; break; + //case "tstzrange": ret = XGDbType.Range | XGDbType.TimestampTz; break; + //case "daterange": ret = XGDbType.Range | XGDbType.Date; break; + + //case "hstore": ret = XGDbType.Hstore; break; + //case "geometry": ret = XGDbType.Geometry; break; + } + return ret ; + //return isarray ? (ret | XGDbType.Array) : ret; + } + + static readonly Dictionary _dicDbToCs = new Dictionary() { + { (int)XGDbType.SmallInt, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") }, + { (int)XGDbType.Int, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") }, + { (int)XGDbType.BigInt, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") }, + { (int)XGDbType.Numeric, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") }, + { (int)XGDbType.Real, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") }, + { (int)XGDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") }, + + { (int)XGDbType.Char, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + { (int)XGDbType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + { (int)XGDbType.LongVarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + + + { (int)XGDbType.DateTime, ("(DateTime?)", "new DateTime({0})", "{0}.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") }, + + + { (int)XGDbType.Bool, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") }, + + + }; + + public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null; + public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null; + public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null; + public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null; + public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null; + public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null; + public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null; + + public List GetDatabases() + { + var sql = @" select DB_NAME from all_databases where DROPED=FALSE AND USER_ID>0"; + var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + return ds.Select(a => a.FirstOrDefault()?.ToString()).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[] { DefaultSchema, tbname[0] }; + var sql = string.Format("select 1 from all_tables a inner join all_schemas b on b.schema_id = a.schema_id where b.schema_name || '.' || a.table_name = '{0}.{1}'", tbname); + + 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 GetTablesByDatabase(params string[] database) => GetTables(database, null,false); + + public List GetTables(string[] database, string tablename, bool ignoreCase) + { + var olddatabase = ""; + using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5))) + { + olddatabase = conn.Value.Database; + } + string[] tbname = null; + string[] dbs = database == null || database.Any() == false ? new[] { olddatabase } : database; + if (string.IsNullOrEmpty(tablename) == false) + { + tbname = _commonUtils.SplitTableName(tablename); + if (tbname?.Length == 1) tbname = new[] { DefaultSchema, tbname[0] }; + dbs = new[] { olddatabase }; + } + var tables = new List(); + + foreach (var db in dbs) + { + if (string.IsNullOrEmpty(db) || string.Compare(db, olddatabase, true) != 0) continue; + + var loc1 = new List(); + var loc2 = new Dictionary(); + var loc3 = new Dictionary>(); + + var sql = $@" +{(tbname == null ? "" : $"select * from (")}select b.schema_name || '.' || a.table_name as ns, +b.schema_name, +a.table_name, +a.comments, +'TABLE' AS type +from all_tables a +inner join all_schemas b on b.schema_id = a.schema_id +where a.IS_SYS=FALSE + +union all + +select b.schema_name || '.' || a.view_name as ns, +b.schema_name, +a.view_name, +a.comments, +'VIEW' AS type +from all_views a +inner join all_schemas b on b.schema_id = a.schema_id +where a.IS_SYS=FALSE +{(tbname == null ? "" : $") ft_dbf where schema_name ={_commonUtils.FormatSql("{0}", tbname[0])} and ft_dbf.Table_Name={_commonUtils.FormatSql("{0}", tbname[1])}")}"; + var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var loc6 = new List(); + var loc66 = new List(); + var loc6_1000 = new List(); + var loc66_1000 = new List(); + foreach (object[] row in ds) + { + var object_id = string.Concat(row[0]); + var owner = string.Concat(row[1]); + var table = string.Concat(row[2]); + var comment = string.Concat(row[3]); + Enum.TryParse(string.Concat(row[4]), out var type); + loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Comment = comment, Type = type }); + loc3.Add(object_id, new Dictionary()); + switch (type) + { + case DbTableType.VIEW: + case DbTableType.TABLE: + loc6_1000.Add(object_id); + if (loc6_1000.Count >= 500) + { + loc6.Add(loc6_1000.ToArray()); + loc6_1000.Clear(); + } + break; + case DbTableType.StoreProcedure: + loc66_1000.Add(object_id); + if (loc66_1000.Count >= 500) + { + loc66.Add(loc66_1000.ToArray()); + loc66_1000.Clear(); + } + break; + } + } + + if (loc6_1000.Count > 0) loc6.Add(loc6_1000.ToArray()); + if (loc66_1000.Count > 0) loc66.Add(loc66_1000.ToArray()); + + if (loc6.Count == 0) return loc1; + var loc8 = new StringBuilder().Append("("); + for (var loc8idx = 0; loc8idx < loc6.Count; loc8idx++) + { + if (loc8idx > 0) loc8.Append(" OR "); + loc8.Append("a.table_name in ("); + for (var loc8idx2 = 0; loc8idx2 < loc6[loc8idx].Length; loc8idx2++) + { + if (loc8idx2 > 0) loc8.Append(","); + loc8.Append($"'{loc6[loc8idx][loc8idx2]}'"); + } + loc8.Append(")"); + } + loc8.Append(")"); + + sql = $@" +select +ns.schema_name || '.' || tb.table_name as id, +a.COL_NAME, +a.TYPE_NAME, +a.SCALE, +case when a.NOT_NULL then '0' else '1' end as is_nullable, +seq.SEQ_ID, +seq.IS_SYS, +seq.MIN_VAL, +seq.STEP_VAL, +a.COMMENTS, +tb.table_name, +a.def_val, +ns.schema_id, +a.`VARYING` +from all_columns as a +left join all_tables tb on a.table_id=tb.table_id +left join all_schemas ns on tb.schema_id = ns.schema_id +left join all_sequences seq on seq.SEQ_ID=a.Serial_ID +where {loc8.ToString().Replace("a.table_name", "ns.schema_name || '.' || tb.table_name")}"; + + ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + var position = 1; + foreach (object[] row in ds) + { + var object_id = string.Concat(row[0]); + var column = string.Concat(row[1]); + var type = string.Concat(row[2]); + var max_length = int.Parse(string.Concat(row[3])); + //var sqlType = string.Concat(row[4]); + var is_nullable = string.Concat(row[5]) == "1"; + + long.TryParse(row[5]?.ToString() ?? "0", out long SEQ_ID); + var SEQ_IS_SYS = (row[6]?.ToString() ?? "false")?.ToLower(); + + var is_identity = (SEQ_ID > 0 && SEQ_IS_SYS == "true"); + var comment = string.Concat(row[9]); + + + var defaultValue = string.Concat(row[11]); + //int attndims = int.Parse(string.Concat(row[8])); + //string typtype = string.Concat(row[9]); + //string owner = string.Concat(row[10]); + //int attnum = int.Parse(string.Concat(row[11])); + + //switch (sqlType.ToLower()) + //{ + // case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break; + // default: max_length *= 8; break; + //} + if (max_length <= 0) max_length = -1; + //if (type.StartsWith("_")) + //{ + // type = type.Substring(1); + // if (attndims == 0) attndims++; + //} + var sqlType = type; + if (max_length > 0) + { + switch (type.ToUpper()) + { + //case "numeric": sqlType += $"({max_length})"; break; + + case "NUMERIC": + { + var data_precision = max_length % 65536; + var data_scale = max_length / 65536; + sqlType += $"({data_scale},{data_precision})"; break; + } + + //case "DATETIME": + // break; + default: + sqlType += $"({max_length})"; break; + } + } + //if (attndims > 0) type += "[]"; + + loc3[object_id].Add(column, new DbColumnInfo + { + Name = column, + MaxLength = max_length, + IsIdentity = is_identity, + IsNullable = is_nullable, + IsPrimary = false, + DbTypeText = type, + DbTypeTextFull = sqlType, + Table = loc2[object_id], + Comment = comment, + DefaultValue = defaultValue, + Position = position + }); + loc3[object_id][column].DbType = this.GetDbType(loc3[object_id][column]); + loc3[object_id][column].CsType = this.GetCsTypeInfo(loc3[object_id][column]); + position++; + } + + sql = $@" +select +ns.SCHEMA_NAME || '.' || t.table_name as table_id, +keys, +a.index_name, +case when a.IS_UNIQUE then 1 else 0 end as IsUnique, +case when a.IS_Primary then 1 else 0 end as IsPrimary, +0, +0, +t.table_name, +ns.SCHEMA_NAME, +a.IS_SYS +from all_INDEXES a +left join all_tables t on a.TABLE_ID=t.TABLE_ID +left join all_schemas ns on t.SCHEMA_ID=ns.SCHEMA_ID +where IS_SYS=false and {loc8.ToString().Replace("a.table_name", "ns.SCHEMA_NAME || '.' || t.table_name")} +"; + ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var indexColumns = new Dictionary>(); + var uniqueColumns = new Dictionary>(); + foreach (object[] row in ds) + { + var object_id = string.Concat(row[0]); + var column = string.Concat(row[1]); + var index_id = string.Concat(row[2]); + var is_unique = string.Concat(row[3]) == "1"; + var is_primary_key = string.Concat(row[4]) == "1"; + var is_clustered = false;//= string.Concat(row[5]) == "1"; + var is_desc = false;//string.Concat(row[6]) == "1"; + //var inkey = string.Concat(row[7]).Split(' '); + //var attnum = int.Parse(string.Concat(row[8])); + //attnum = int.Parse(inkey[attnum - 1]); + //foreach (string tc in loc3[object_id].Keys) //bug: https://github.com/2881099/FreeSql.Wiki.VuePress/issues/9 + //{ + // if (loc3[object_id][tc].DbTypeText.EndsWith("[]")) + // { + // column = tc; + // break; + // } + //} + if (loc3.ContainsKey(object_id) == false || loc3[object_id].ContainsKey(column) == false) continue; + var loc9 = loc3[object_id][column]; + if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key; + + Dictionary loc10 = null; + DbIndexInfo loc11 = null; + if (!indexColumns.TryGetValue(object_id, out loc10)) + indexColumns.Add(object_id, loc10 = new Dictionary()); + if (!loc10.TryGetValue(index_id, out loc11)) + loc10.Add(index_id, loc11 = new DbIndexInfo()); + loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc }); + if (is_unique && !is_primary_key) + { + if (!uniqueColumns.TryGetValue(object_id, out loc10)) + uniqueColumns.Add(object_id, loc10 = new Dictionary()); + if (!loc10.TryGetValue(index_id, out loc11)) + loc10.Add(index_id, loc11 = new DbIndexInfo()); + loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc }); + } + } + foreach (var object_id in indexColumns.Keys) + { + foreach (var column in indexColumns[object_id]) + loc2[object_id].IndexesDict.Add(column.Key, column.Value); + } + foreach (var object_id in uniqueColumns.Keys) + { + foreach (var column in uniqueColumns[object_id]) + { + column.Value.Columns.Sort((c1, c2) => c1.Column.Name.CompareTo(c2.Column.Name)); + loc2[object_id].UniquesDict.Add(column.Key, column.Value); + } + } + + if (tbname == null) + { + sql = $@" +select +schema_name || '.' || b.table_name as table_id, +cons_name as FKId, +cons_type, +define +from all_constraints as a +left join all_tables as b on a.Table_ID=b.Table_ID +left Join all_SCHEMAS AS c on b.SCHEMA_ID=c.SCHEMA_ID +where IS_SYS=false AND {loc8.ToString().Replace("a.table_name", "schema_name || '.' || b.table_name")} +"; + ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var fkColumns = new Dictionary>(); + foreach (object[] row in ds) + { + var table_id = string.Concat(row[0]); + var column = row[3] as string[]; + var fk_id = string.Concat(row[1]); + var ref_table_id = string.Concat(row[0]); + var is_foreign_key = string.Concat(row[2]) == "F"; + var referenced_column = row[5] as string[]; + //var referenced_db = string.Concat(row[6]); + //var referenced_table = string.Concat(row[7]); + + if (loc2.ContainsKey(ref_table_id) == false) continue; + + Dictionary loc12 = null; + DbForeignInfo loc13 = null; + if (!fkColumns.TryGetValue(table_id, out loc12)) + fkColumns.Add(table_id, loc12 = new Dictionary()); + if (!loc12.TryGetValue(fk_id, out loc13)) + loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[table_id], ReferencedTable = loc2[ref_table_id] }); + + for (int a = 0; a < column.Length; a++) + { + loc13.Columns.Add(loc3[table_id][column[a]]); + loc13.ReferencedColumns.Add(loc3[ref_table_id][referenced_column[a]]); + } + } + foreach (var table_id in fkColumns.Keys) + foreach (var fk in fkColumns[table_id]) + loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value); + } + + foreach (var table_id in loc3.Keys) + { + foreach (var loc5 in loc3[table_id].Values) + { + loc2[table_id].Columns.Add(loc5); + if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5); + if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5); + } + } + foreach (var loc4 in loc2.Values) + { + //if (loc4.Primarys.Count == 0 && loc4.UniquesDict.Count > 0) + //{ + // foreach (var loc5 in loc4.UniquesDict.First().Value.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(); + tables.AddRange(loc1); + } + return tables; + } + + public List GetEnumsByDatabase(params string[] database) + { + return new List(); + } + + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Xugu/XuguExpression.cs b/Providers/FreeSql.Provider.Xugu/XuguExpression.cs new file mode 100644 index 00000000..7dc206b7 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguExpression.cs @@ -0,0 +1,689 @@ +using FreeSql.Internal; +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Text.RegularExpressions; + +namespace FreeSql.Xugu +{ + class XuguExpression : CommonExpression + { + + public XuguExpression(CommonUtils common) : base(common) { } + + public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + switch (exp.NodeType) + { + case ExpressionType.ArrayLength: + var arrOper = (exp as UnaryExpression)?.Operand; + var arrOperExp = getExp(arrOper); + if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)"; + if (arrOper.Type == typeof(byte[])) return $"octet_length({getExp(arrOper)})"; + return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end"; + case ExpressionType.Convert: + var operandExp = (exp as UnaryExpression)?.Operand; + var gentype = exp.Type.NullableTypeOrThis(); + if (gentype != operandExp.Type.NullableTypeOrThis()) + { + switch (exp.Type.NullableTypeOrThis().ToString()) + { + case "System.Boolean": return $"(({getExp(operandExp)})::varchar not in ('0','false','f','no'))"; + case "System.Byte": return $"({getExp(operandExp)})::int2"; + case "System.Char": return $"substr(({getExp(operandExp)})::char, 1, 1)"; + case "System.DateTime": return ExpressionConstDateTime(operandExp) ?? $"({getExp(operandExp)})::timestamp"; + case "System.Decimal": return $"({getExp(operandExp)})::numeric"; + case "System.Double": return $"({getExp(operandExp)})::float8"; + case "System.Int16": return $"({getExp(operandExp)})::int2"; + case "System.Int32": return $"({getExp(operandExp)})::int4"; + case "System.Int64": return $"({getExp(operandExp)})::int8"; + case "System.SByte": return $"({getExp(operandExp)})::int2"; + case "System.Single": return $"({getExp(operandExp)})::float4"; + case "System.String": return $"({getExp(operandExp)})::text"; + case "System.UInt16": return $"({getExp(operandExp)})::int2"; + case "System.UInt32": return $"({getExp(operandExp)})::int4"; + case "System.UInt64": return $"({getExp(operandExp)})::int8"; + case "System.Guid": return $"({getExp(operandExp)})::uuid"; + } + } + break; + case ExpressionType.Call: + var callExp = exp as MethodCallExpression; + + switch (callExp.Method.Name) + { + case "Parse": + case "TryParse": + switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) + { + case "System.Boolean": return $"(({getExp(callExp.Arguments[0])})::varchar not in ('0','false','f','no'))"; + case "System.Byte": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.Char": return $"substr(({getExp(callExp.Arguments[0])})::char, 1, 1)"; + case "System.DateTime": return ExpressionConstDateTime(callExp.Arguments[0]) ?? $"({getExp(callExp.Arguments[0])})::timestamp"; + case "System.Decimal": return $"({getExp(callExp.Arguments[0])})::numeric"; + case "System.Double": return $"({getExp(callExp.Arguments[0])})::float8"; + case "System.Int16": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.Int32": return $"({getExp(callExp.Arguments[0])})::int4"; + case "System.Int64": return $"({getExp(callExp.Arguments[0])})::int8"; + case "System.SByte": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.Single": return $"({getExp(callExp.Arguments[0])})::float4"; + case "System.UInt16": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.UInt32": return $"({getExp(callExp.Arguments[0])})::int4"; + case "System.UInt64": return $"({getExp(callExp.Arguments[0])})::int8"; + case "System.Guid": return $"({getExp(callExp.Arguments[0])})::uuid"; + } + break; + case "NewGuid": + return null; + case "Next": + if (callExp.Object?.Type == typeof(Random)) return "(random()*1000000000)::int4"; + return null; + case "NextDouble": + if (callExp.Object?.Type == typeof(Random)) return "random()"; + return null; + case "Random": + if (callExp.Method.DeclaringType.IsNumberType()) return "random()"; + return null; + case "ToString": + if (callExp.Object != null) return callExp.Arguments.Count == 0 ? $"({getExp(callExp.Object)})::text" : null; + return null; + } + + var objExp = callExp.Object; + var objType = objExp?.Type; + if (objType?.FullName == "System.Byte[]") return null; + + var argIndex = 0; + if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) + { + objExp = callExp.Arguments.FirstOrDefault(); + objType = objExp?.Type; + argIndex++; + + if (objType == typeof(string)) + { + switch (callExp.Method.Name) + { + case "First": + case "FirstOrDefault": + return $"substr({getExp(callExp.Arguments[0])}, 1, 1)"; + } + } + } + if (objType == null) objType = callExp.Method.DeclaringType; + if (objType != null || objType.IsArrayOrList()) + { + string left = null; + switch (objType.FullName) + { + case "Newtonsoft.Json.Linq.JToken": + case "Newtonsoft.Json.Linq.JObject": + case "Newtonsoft.Json.Linq.JArray": + left = objExp == null ? null : getExp(objExp); + switch (callExp.Method.Name) + { + case "get_Item": return $"{left}->{getExp(callExp.Arguments[argIndex])}"; + case "Any": return $"(jsonb_array_length(coalesce({left},'[]')) > 0)"; + case "Contains": + var json = getExp(callExp.Arguments[argIndex]); + if (objType == typeof(JArray)) + return $"(coalesce({left},'[]') ? ({json})::text)"; + if (json.StartsWith("'") && json.EndsWith("'")) + return $"(coalesce({left},'{{}}') @> {_common.FormatSql("{0}", JToken.Parse(json.Trim('\'')))})"; + return $"(coalesce({left},'{{}}') @> ({json})::jsonb)"; + case "ContainsKey": return $"(coalesce({left},'{{}}') ? {getExp(callExp.Arguments[argIndex])})"; + case "Concat": + var right2 = getExp(callExp.Arguments[argIndex]); + return $"(coalesce({left},'{{}}') || {right2})"; + case "LongCount": + case "Count": return $"jsonb_array_length(coalesce({left},'[]'))"; + case "Parse": + var json2 = getExp(callExp.Arguments[argIndex]); + if (json2.StartsWith("'") && json2.EndsWith("'")) return _common.FormatSql("{0}", JToken.Parse(json2.Trim('\''))); + return $"({json2})::jsonb"; + } + break; + } + if (objType == typeof(Dictionary)) + { + left = objExp == null ? null : getExp(objExp); + switch (callExp.Method.Name) + { + case "get_Item": return $"{left}->{getExp(callExp.Arguments[argIndex])}"; + case "Contains": + var right = getExp(callExp.Arguments[argIndex]); + return $"({left} @> ({right}))"; + case "ContainsKey": return $"({left} ? {getExp(callExp.Arguments[argIndex])})"; + case "Concat": return $"({left} || {getExp(callExp.Arguments[argIndex])})"; + case "GetLength": + case "GetLongLength": + case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end"; + case "Keys": return $"akeys({left})"; + case "Values": return $"avals({left})"; + } + } + switch (callExp.Method.Name) + { + case "Any": + left = objExp == null ? null : getExp(objExp); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + return $"(case when {left} is null then 0 else array_length({left},1) end > 0)"; + case "Contains": + tsc.SetMapColumnTmp(null); + var args1 = getExp(callExp.Arguments[argIndex]); + var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp); + var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug + tsc.isNotSetMapColumnTmp = true; + left = objExp == null ? null : getExp(objExp); + tsc.isNotSetMapColumnTmp = false; + tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType); + if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams); + //判断 in 或 array @> array + if (left.StartsWith("array[") || left.EndsWith("]")) + return $"({args1}) in ({left.Substring(6, left.Length - 7)})"; + if (left.StartsWith("(") || left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格 + return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})"; + if (args1.StartsWith("(") || args1.EndsWith(")")) args1 = $"array[{args1.TrimStart('(').TrimEnd(')')}]"; + args1 = $"array[{args1}]"; + if (objExp != null) + { + var dbinfo = _common._orm.CodeFirst.GetDbInfo(objExp.Type); + if (dbinfo != null) args1 = $"{args1}::{dbinfo.dbtype}"; + } + return $"({left} @> {args1})"; + case "Concat": + left = objExp == null ? null : getExp(objExp); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + var right2 = getExp(callExp.Arguments[argIndex]); + if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]"; + return $"({left} || {right2})"; + case "GetLength": + case "GetLongLength": + case "Length": + case "Count": + left = objExp == null ? null : getExp(objExp); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + return $"case when {left} is null then 0 else array_length({left},1) end"; + } + } + break; + case ExpressionType.MemberAccess: + var memExp = exp as MemberExpression; + var memParentExp = memExp.Expression?.Type; + if (memParentExp?.FullName == "System.Byte[]") return null; + if (memParentExp != null) + { + if (memParentExp.IsArray == true) + { + var left = getExp(memExp.Expression); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + switch (memExp.Member.Name) + { + case "Length": + case "Count": return $"case when {left} is null then 0 else array_length({left},1) end"; + } + } + switch (memParentExp.FullName) + { + case "Newtonsoft.Json.Linq.JToken": + case "Newtonsoft.Json.Linq.JObject": + case "Newtonsoft.Json.Linq.JArray": + var left = getExp(memExp.Expression); + switch (memExp.Member.Name) + { + case "Count": return $"jsonb_array_length(coalesce({left},'[]'))"; + } + break; + } + if (memParentExp == typeof(Dictionary)) + { + var left = getExp(memExp.Expression); + switch (memExp.Member.Name) + { + case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end"; + case "Keys": return $"akeys({left})"; + case "Values": return $"avals({left})"; + } + } + } + break; + case ExpressionType.NewArrayInit: + var arrExp = exp as NewArrayExpression; + var arrSb = new StringBuilder(); + arrSb.Append("array["); + for (var a = 0; a < arrExp.Expressions.Count; a++) + { + if (a > 0) arrSb.Append(","); + arrSb.Append(getExp(arrExp.Expressions[a])); + } + if (arrSb.Length == 1) arrSb.Append("NULL"); + return arrSb.Append("]").ToString(); + case ExpressionType.ListInit: + var listExp = exp as ListInitExpression; + var listSb = new StringBuilder(); + listSb.Append("("); + for (var a = 0; a < listExp.Initializers.Count; a++) + { + if (listExp.Initializers[a].Arguments.Any() == false) continue; + if (a > 0) listSb.Append(","); + listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault())); + } + if (listSb.Length == 1) listSb.Append("NULL"); + return listSb.Append(")").ToString(); + case ExpressionType.New: + var newExp = exp as NewExpression; + if (typeof(IList).IsAssignableFrom(newExp.Type)) + { + if (newExp.Arguments.Count == 0) return "(NULL)"; + if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)"; + return getExp(newExp.Arguments[0]); + } + return null; + } + return null; + } + + public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Empty": return "''"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Length": return $"char_length({left})"; + } + return null; + } + public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Now": return _common.Now; + case "UtcNow": return _common.NowUtc; + case "Today": return "current_date"; + case "MinValue": return "'0001/1/1 0:00:00'::timestamp"; + case "MaxValue": return "'9999/12/31 23:59:59'::timestamp"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Date": return $"({left})::date"; + case "TimeOfDay": return $"(extract(epoch from ({left})::time)*1000000)"; + case "DayOfWeek": return $"extract(dow from ({left})::timestamp)"; + case "Day": return $"extract(day from ({left})::timestamp)"; + case "DayOfYear": return $"extract(doy from ({left})::timestamp)"; + case "Month": return $"extract(month from ({left})::timestamp)"; + case "Year": return $"extract(year from ({left})::timestamp)"; + case "Hour": return $"extract(hour from ({left})::timestamp)"; + case "Minute": return $"extract(minute from ({left})::timestamp)"; + case "Second": return $"extract(second from ({left})::timestamp)"; + case "Millisecond": return $"(extract(milliseconds from ({left})::timestamp)-extract(second from ({left})::timestamp)*1000)"; + case "Ticks": return $"(extract(epoch from ({left})::timestamp)*10000000+621355968000000000)"; + } + return null; + } + public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Zero": return "0"; + case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10 + case "MaxValue": return "922337203685477580"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Days": return $"floor(({left})/{(long)1000000 * 60 * 60 * 24})"; + case "Hours": return $"floor(({left})/{(long)1000000 * 60 * 60}%24)"; + case "Milliseconds": return $"(floor(({left})/1000)::int8%1000)"; + case "Minutes": return $"(floor(({left})/{(long)1000000 * 60})::int8%60)"; + case "Seconds": return $"(floor(({left})/1000000)::int8%60)"; + case "Ticks": return $"(({left})*10)"; + case "TotalDays": return $"(({left})/{(long)1000000 * 60 * 60 * 24})"; + case "TotalHours": return $"(({left})/{(long)1000000 * 60 * 60})"; + case "TotalMilliseconds": return $"(({left})/1000)"; + case "TotalMinutes": return $"(({left})/{(long)1000000 * 60})"; + case "TotalSeconds": return $"(({left})/1000000)"; + } + return null; + } + + public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "IsNullOrEmpty": + var arg1 = getExp(exp.Arguments[0]); + return $"({arg1} is null or {arg1} = '')"; + case "IsNullOrWhiteSpace": + var arg2 = getExp(exp.Arguments[0]); + return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')"; + case "Concat": + return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null); + case "Format": + if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception(CoreStrings.Not_Implemented_Expression_ParameterUseConstant(exp,exp.Arguments[0])); + var expArgsHack = exp.Arguments.Count == 2 && exp.Arguments[1].NodeType == ExpressionType.NewArrayInit ? + (exp.Arguments[1] as NewArrayExpression).Expressions : exp.Arguments.Where((a, z) => z > 0); + //3个 {} 时,Arguments 解析出来是分开的 + //4个 {} 时,Arguments[1] 只能解析这个出来,然后里面是 NewArray [] + var expArgs = expArgsHack.Select(a => + { + var atype = (a as UnaryExpression)?.Operand.Type.NullableTypeOrThis() ?? a.Type.NullableTypeOrThis(); + if (atype == typeof(string)) return $"'||{_common.IsNull(ExpressionLambdaToSql(a, tsc), "''")}||'"; + return $"'||{_common.IsNull($"({ExpressionLambdaToSql(a, tsc)})::text", "''")}||'"; + }).ToArray(); + return string.Format(ExpressionLambdaToSql(exp.Arguments[0], tsc), expArgs); + case "Join": + if (exp.IsStringJoin(out var tolistObjectExp, out var toListMethod, out var toListArgs1)) + { + var newToListArgs0 = Expression.Call(tolistObjectExp, toListMethod, + Expression.Lambda( + Expression.Call( + typeof(SqlExtExtensions).GetMethod("StringJoinPgsqlGroupConcat"), + Expression.Convert(toListArgs1.Body, typeof(object)), + Expression.Convert(exp.Arguments[0], typeof(object))), + toListArgs1.Parameters)); + var newToListSql = getExp(newToListArgs0); + return newToListSql; + } + break; + } + } + else + { + var left = getExp(exp.Object); + switch (exp.Method.Name) + { + case "StartsWith": + case "EndsWith": + case "Contains": + var args0Value = getExp(exp.Arguments[0]); + if (args0Value == "NULL") return $"({left}) IS NULL"; + if (args0Value.Contains("%")) + { + if (exp.Method.Name == "StartsWith") return $"strpos({args0Value}, {left}) = 1"; + if (exp.Method.Name == "EndsWith") return $"strpos({args0Value}, {left}) = char_length({args0Value})"; + return $"strpos({args0Value}, {left}) > 0"; + } + var likeOpt = "LIKE"; + if (exp.Arguments.Count > 1) + { + if (exp.Arguments[1].Type == typeof(bool) || + exp.Arguments[1].Type == typeof(StringComparison)) likeOpt = "ILIKE"; + } + if (exp.Method.Name == "StartsWith") return $"({left}) {likeOpt} {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(({args0Value})::text || '%')")}"; + if (exp.Method.Name == "EndsWith") return $"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::text)")}"; + if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) {likeOpt} {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}"; + return $"({left}) {likeOpt} ('%' || ({args0Value})::text || '%')"; + case "ToLower": return $"lower({left})"; + case "ToUpper": return $"upper({left})"; + case "Substring": + var substrArgs1 = getExp(exp.Arguments[0]); + if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString(); + else substrArgs1 += "+1"; + if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})"; + return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})"; + case "IndexOf": return $"(strpos({left}, {getExp(exp.Arguments[0])})-1)"; + case "PadLeft": + if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})"; + return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "PadRight": + if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})"; + return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Trim": + case "TrimStart": + case "TrimEnd": + if (exp.Arguments.Count == 0) + { + if (exp.Method.Name == "Trim") return $"trim({left})"; + if (exp.Method.Name == "TrimStart") return $"ltrim({left})"; + if (exp.Method.Name == "TrimEnd") return $"rtrim({left})"; + } + var trimArg1 = ""; + var trimArg2 = ""; + foreach (var argsTrim02 in exp.Arguments) + { + var argsTrim01s = new[] { argsTrim02 }; + if (argsTrim02.NodeType == ExpressionType.NewArrayInit) + { + var arritem = argsTrim02 as NewArrayExpression; + argsTrim01s = arritem.Expressions.ToArray(); + } + foreach (var argsTrim01 in argsTrim01s) + { + var trimChr = getExp(argsTrim01).Trim('\''); + if (trimChr.Length == 1) trimArg1 += trimChr; + else trimArg2 += $" || ({trimChr})"; + } + } + if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + return left; + case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end"; + case "Equals": return $"({left} = ({getExp(exp.Arguments[0])})::text)"; + } + } + return null; + } + public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + switch (exp.Method.Name) + { + case "Abs": return $"abs({getExp(exp.Arguments[0])})"; + case "Sign": return $"sign({getExp(exp.Arguments[0])})"; + case "Floor": return $"floor({getExp(exp.Arguments[0])})"; + case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})"; + case "Round": + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + return $"round({getExp(exp.Arguments[0])})"; + case "Exp": return $"exp({getExp(exp.Arguments[0])})"; + case "Log": return $"log({getExp(exp.Arguments[0])})"; + case "Log10": return $"log10({getExp(exp.Arguments[0])})"; + case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})"; + case "Cos": return $"cos({getExp(exp.Arguments[0])})"; + case "Sin": return $"sin({getExp(exp.Arguments[0])})"; + case "Tan": return $"tan({getExp(exp.Arguments[0])})"; + case "Acos": return $"acos({getExp(exp.Arguments[0])})"; + case "Asin": return $"asin({getExp(exp.Arguments[0])})"; + case "Atan": return $"atan({getExp(exp.Arguments[0])})"; + case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Truncate": return $"trunc({getExp(exp.Arguments[0])}, 0)"; + } + return null; + } + public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "Compare": return $"extract(epoch from ({getExp(exp.Arguments[0])})::timestamp-({getExp(exp.Arguments[1])})::timestamp)"; + case "DaysInMonth": return $"extract(day from ({getExp(exp.Arguments[0])} || '-' || {getExp(exp.Arguments[1])} || '-01')::timestamp+'1 month'::interval-'1 day'::interval)"; + case "Equals": return $"(({getExp(exp.Arguments[0])})::timestamp = ({getExp(exp.Arguments[1])})::timestamp)"; + + case "IsLeapYear": + var isLeapYearArgs1 = getExp(exp.Arguments[0]); + return $"(({isLeapYearArgs1})::int8%4=0 AND ({isLeapYearArgs1})::int8%100<>0 OR ({isLeapYearArgs1})::int8%400=0)"; + + case "Parse": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"({getExp(exp.Arguments[0])})::timestamp"; + case "ParseExact": + case "TryParse": + case "TryParseExact": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"({getExp(exp.Arguments[0])})::timestamp"; + } + } + else + { + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); + switch (exp.Method.Name) + { + case "Add": return $"(({left})::timestamp+((({args1})/1000)||' milliseconds')::interval)"; + case "AddDays": return $"(({left})::timestamp+(({args1})||' day')::interval)"; + case "AddHours": return $"(({left})::timestamp+(({args1})||' hour')::interval)"; + case "AddMilliseconds": return $"(({left})::timestamp+(({args1})||' milliseconds')::interval)"; + case "AddMinutes": return $"(({left})::timestamp+(({args1})||' minute')::interval)"; + case "AddMonths": return $"(({left})::timestamp+(({args1})||' month')::interval)"; + case "AddSeconds": return $"(({left})::timestamp+(({args1})||' second')::interval)"; + case "AddTicks": return $"(({left})::timestamp+(({args1})/10||' microseconds')::interval)"; + case "AddYears": return $"(({left})::timestamp+(({args1})||' year')::interval)"; + case "Subtract": + switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName) + { + case "System.DateTime": return $"(extract(epoch from ({left})::timestamp-({args1})::timestamp)*1000000)"; + case "System.TimeSpan": return $"(({left})::timestamp-((({args1})/1000)||' milliseconds')::interval)"; + } + break; + case "Equals": return $"({left} = ({args1})::timestamp)"; + case "CompareTo": return $"extract(epoch from ({left})::timestamp-({args1})::timestamp)"; + case "ToString": + if (left.EndsWith("::timestamp") == false) left = $"({left})::timestamp"; + if (exp.Arguments.Count == 0) return $"to_char({left},'YYYY-MM-DD HH24:MI:SS.US')"; + switch (args1) + { + case "'yyyy-MM-dd HH:mm:ss'": return $"to_char({left},'YYYY-MM-DD HH24:MI:SS')"; + case "'yyyy-MM-dd HH:mm'": return $"to_char({left},'YYYY-MM-DD HH24:MI')"; + case "'yyyy-MM-dd HH'": return $"to_char({left},'YYYY-MM-DD HH24')"; + case "'yyyy-MM-dd'": return $"to_char({left},'YYYY-MM-DD')"; + case "'yyyy-MM'": return $"to_char({left},'YYYY-MM')"; + case "'yyyyMMddHHmmss'": return $"to_char({left},'YYYYMMDDHH24MISS')"; + case "'yyyyMMddHHmm'": return $"to_char({left},'YYYYMMDDHH24MI')"; + case "'yyyyMMddHH'": return $"to_char({left},'YYYYMMDDHH24')"; + case "'yyyyMMdd'": return $"to_char({left},'YYYYMMDD')"; + case "'yyyyMM'": return $"to_char({left},'YYYYMM')"; + case "'yyyy'": return $"to_char({left},'YYYY')"; + case "'HH:mm:ss'": return $"to_char({left},'HH24:MI:SS')"; + } + args1 = Regex.Replace(args1, "(yyyy|yy|MM|dd|HH|hh|mm|ss|tt)", m => + { + switch (m.Groups[1].Value) + { + case "yyyy": return $"YYYY"; + case "yy": return $"YY"; + case "MM": return $"%_a1"; + case "dd": return $"%_a2"; + case "HH": return $"%_a3"; + case "hh": return $"%_a4"; + case "mm": return $"%_a5"; + case "ss": return $"SS"; + case "tt": return $"%_a6"; + } + return m.Groups[0].Value; + }); + var argsFinds = new[] { "YYYY", "YY", "%_a1", "%_a2", "%_a3", "%_a4", "%_a5", "SS", "%_a6" }; + var argsSpts = Regex.Split(args1, "(M|d|H|h|m|s|t)"); + for (var a = 0; a < argsSpts.Length; a++) + { + switch (argsSpts[a]) + { + case "M": argsSpts[a] = $"ltrim(to_char({left},'MM'),'0')"; break; + case "d": argsSpts[a] = $"case when substr(to_char({left},'DD'),1,1) = '0' then substr(to_char({left},'DD'),2,1) else to_char({left},'DD') end"; break; + case "H": argsSpts[a] = $"case when substr(to_char({left},'HH24'),1,1) = '0' then substr(to_char({left},'HH24'),2,1) else to_char({left},'HH24') end"; break; + case "h": argsSpts[a] = $"case when substr(to_char({left},'HH12'),1,1) = '0' then substr(to_char({left},'HH12'),2,1) else to_char({left},'HH12') end"; break; + case "m": argsSpts[a] = $"case when substr(to_char({left},'MI'),1,1) = '0' then substr(to_char({left},'MI'),2,1) else to_char({left},'MI') end"; break; + case "s": argsSpts[a] = $"case when substr(to_char({left},'SS'),1,1) = '0' then substr(to_char({left},'SS'),2,1) else to_char({left},'SS') end"; break; + case "t": argsSpts[a] = $"rtrim(to_char({left},'AM'),'M')"; break; + default: + var argsSptsA = argsSpts[a]; + if (argsSptsA.StartsWith("'")) argsSptsA = argsSptsA.Substring(1); + if (argsSptsA.EndsWith("'")) argsSptsA = argsSptsA.Remove(argsSptsA.Length - 1); + argsSpts[a] = argsFinds.Any(m => argsSptsA.Contains(m)) ? $"to_char({left},'{argsSptsA}')" : $"'{argsSptsA}'"; + break; + } + } + if (argsSpts.Length > 0) args1 = $"({string.Join(" || ", argsSpts.Where(a => a != "''"))})"; + return args1.Replace("%_a1", "MM").Replace("%_a2", "DD").Replace("%_a3", "HH24").Replace("%_a4", "HH12").Replace("%_a5", "MI").Replace("%_a6", "AM"); + } + } + return null; + } + public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})"; + case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})"; + case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)"; + case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})"; + case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)"; + case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)"; + case "Parse": return $"({getExp(exp.Arguments[0])})::int8"; + case "ParseExact": + case "TryParse": + case "TryParseExact": return $"({getExp(exp.Arguments[0])})::int8"; + } + } + else + { + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); + switch (exp.Method.Name) + { + case "Add": return $"({left}+{args1})"; + case "Subtract": return $"({left}-({args1}))"; + case "Equals": return $"({left} = {args1})"; + case "CompareTo": return $"({left}-({args1}))"; + case "ToString": return $"({left})::varchar"; + } + } + return null; + } + public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "ToBoolean": return $"(({getExp(exp.Arguments[0])})::varchar not in ('0','false','f','no'))"; + case "ToByte": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToChar": return $"substr(({getExp(exp.Arguments[0])})::char, 1, 1)"; + case "ToDateTime": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"({getExp(exp.Arguments[0])})::timestamp"; + case "ToDecimal": return $"({getExp(exp.Arguments[0])})::numeric"; + case "ToDouble": return $"({getExp(exp.Arguments[0])})::float8"; + case "ToInt16": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToInt32": return $"({getExp(exp.Arguments[0])})::int4"; + case "ToInt64": return $"({getExp(exp.Arguments[0])})::int8"; + case "ToSByte": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToSingle": return $"({getExp(exp.Arguments[0])})::float4"; + case "ToString": return $"({getExp(exp.Arguments[0])})::text"; + case "ToUInt16": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToUInt32": return $"({getExp(exp.Arguments[0])})::int4"; + case "ToUInt64": return $"({getExp(exp.Arguments[0])})::int8"; + } + } + return null; + } + } +} diff --git a/Providers/FreeSql.Provider.Xugu/XuguExtensions.cs b/Providers/FreeSql.Provider.Xugu/XuguExtensions.cs new file mode 100644 index 00000000..2481048f --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguExtensions.cs @@ -0,0 +1,20 @@ +using FreeSql; +using FreeSql.Xugu.Curd; +using System.Data; +using System.Linq.Expressions; +using System.Text; +using XuguClient; + +public static partial class FreeSqlXuguGlobalExtensions +{ + + /// + /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换 + /// + /// + /// + /// + public static string FormatXuguSQL(this string that, params object[] args) => _xgsqlAdo.Addslashes(that, args); + static FreeSql.Xugu.XuguAdo _xgsqlAdo = new FreeSql.Xugu.XuguAdo(); + +} diff --git a/Providers/FreeSql.Provider.Xugu/XuguProvider.cs b/Providers/FreeSql.Provider.Xugu/XuguProvider.cs new file mode 100644 index 00000000..92122c19 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguProvider.cs @@ -0,0 +1,131 @@ +using FreeSql.Internal; +using FreeSql.Internal.CommonProvider; +using FreeSql.Xugu.Curd; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Data.Common; +using System.Linq.Expressions; +using System.Numerics; +using System.Reflection; +using System.Threading; + +namespace FreeSql.Xugu +{ + + public class XuguProvider : BaseDbProvider, IFreeSql + { + static XuguProvider() + { + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BigInteger)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BitArray)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPoint)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlLine)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlLSeg)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlBox)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPath)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPolygon)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlCircle)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof((IPAddress Address, int Subnet))] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(IPAddress)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PhysicalAddress)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange)] = true; + + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisPoint)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisLineString)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisPolygon)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiPoint)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiLineString)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiPolygon)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisGeometry)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisGeometryCollection)] = true; + + + + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(Dictionary)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JToken)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JObject)] = true; + //Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JArray)] = true; + + var MethodJTokenFromObject = typeof(JToken).GetMethod("FromObject", new[] { typeof(object) }); + var MethodJObjectFromObject = typeof(JObject).GetMethod("FromObject", new[] { typeof(object) }); + var MethodJArrayFromObject = typeof(JArray).GetMethod("FromObject", new[] { typeof(object) }); + var MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) }); + var MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) }); + var MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) }); + var MethodJsonConvertDeserializeObject = typeof(JsonConvert).GetMethod("DeserializeObject", new[] { typeof(string), typeof(Type) }); + var MethodToString = typeof(Utils).GetMethod("ToStringConcat", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(object) }, null); + Utils.GetDataReaderValueBlockExpressionSwitchTypeFullName.Add((LabelTarget returnTarget, Expression valueExp, Type type) => + { + switch (type.FullName) + { + case "Newtonsoft.Json.Linq.JToken": + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJTokenParse, Expression.Convert(valueExp, typeof(string))), typeof(JToken))), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJTokenFromObject, valueExp), typeof(JToken)))); + case "Newtonsoft.Json.Linq.JObject": + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJObjectParse, Expression.Convert(valueExp, typeof(string))), typeof(JObject))), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJObjectFromObject, valueExp), typeof(JObject)))); + case "Newtonsoft.Json.Linq.JArray": + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJArrayParse, Expression.Convert(valueExp, typeof(string))), typeof(JArray))), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJArrayFromObject, valueExp), typeof(JArray)))); + //case "Npgsql.LegacyPostgis.PostgisGeometry": + // return Expression.Return(returnTarget, valueExp); + //case "NetTopologySuite.Geometries.Geometry": + // return Expression.Return(returnTarget, valueExp); + } + if (typeof(IList).IsAssignableFrom(type)) + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJsonConvertDeserializeObject, Expression.Convert(valueExp, typeof(string)), Expression.Constant(type, typeof(Type))), type)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJsonConvertDeserializeObject, Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(string)), Expression.Constant(type, typeof(Type))), type))); + return null; + }); + + Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) }); + } + + public override ISelect CreateSelectProvider(object dywhere) => new XuguSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public override IInsert CreateInsertProvider() => new XuguInsert(this, this.InternalCommonUtils, this.InternalCommonExpression); + public override IUpdate CreateUpdateProvider(object dywhere) => new XuguUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public override IDelete CreateDeleteProvider(object dywhere) => new XuguDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public override IInsertOrUpdate CreateInsertOrUpdateProvider() => new XuguInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); + + public XuguProvider(string masterConnectionString, string[] slaveConnectionString, Func connectionFactory = null) + { + this.InternalCommonUtils = new XuguUtils(this); + this.InternalCommonExpression = new XuguExpression(this.InternalCommonUtils); + + this.Ado = new XuguAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory); + this.Aop = new AopProvider(); + + this.DbFirst = new XuguDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression); + this.CodeFirst = new XuguCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression); + + //this.Aop.AuditDataReader += (_, e) => + //{ + // var dbtype = e.DataReader.GetDataTypeName(e.Index); + // var m = Regex.Match(dbtype, @"numeric\((\d+)\)", RegexOptions.IgnoreCase); + // if (m.Success && int.Parse(m.Groups[1].Value) > 19) + // e.Value = e.DataReader.GetFieldValue(e.Index); + //}; + } + + ~XuguProvider() => this.Dispose(); + int _disposeCounter; + public override void Dispose() + { + if (Interlocked.Increment(ref _disposeCounter) != 1) return; + (this.Ado as AdoProvider)?.Dispose(); + } + } +} diff --git a/Providers/FreeSql.Provider.Xugu/XuguUtils.cs b/Providers/FreeSql.Provider.Xugu/XuguUtils.cs new file mode 100644 index 00000000..6ea19420 --- /dev/null +++ b/Providers/FreeSql.Provider.Xugu/XuguUtils.cs @@ -0,0 +1,185 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data.Common; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Text; +using XuguClient; + +namespace FreeSql.Xugu +{ + + class XuguUtils : CommonUtils + { + public XuguUtils(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; + } + 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)}\""; + } + static Dictionary> dicGetParamterValue = new Dictionary> { + //{ typeof(JToken).FullName, a => string.Concat(a) }, { typeof(JToken[]).FullName, a => getParamterArrayValue(typeof(string), a, null) }, + //{ typeof(JObject).FullName, a => string.Concat(a) }, { typeof(JObject[]).FullName, a => getParamterArrayValue(typeof(string), a, null) }, + //{ typeof(JArray).FullName, a => string.Concat(a) }, { typeof(JArray[]).FullName, a => getParamterArrayValue(typeof(string), a, null) }, + { typeof(uint).FullName, a => long.Parse(string.Concat(a)) }, { typeof(uint[]).FullName, a => getParamterArrayValue(typeof(long), a, 0) }, { typeof(uint?[]).FullName, a => getParamterArrayValue(typeof(long?), a, null) }, + { typeof(ulong).FullName, a => decimal.Parse(string.Concat(a)) }, { typeof(ulong[]).FullName, a => getParamterArrayValue(typeof(decimal), a, 0) }, { typeof(ulong?[]).FullName, a => getParamterArrayValue(typeof(decimal?), a, null) }, + { typeof(ushort).FullName, a => int.Parse(string.Concat(a)) }, { typeof(ushort[]).FullName, a => getParamterArrayValue(typeof(int), a, 0) }, { typeof(ushort?[]).FullName, a => getParamterArrayValue(typeof(int?), a, null) }, + { typeof(byte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(byte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(byte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) }, + { typeof(sbyte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(sbyte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(sbyte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) }, + { typeof(char).FullName, a => string.Concat(a).Replace('\0', ' ').ToCharArray().FirstOrDefault() }, + { typeof(BigInteger).FullName, a => BigInteger.Parse(string.Concat(a), System.Globalization.NumberStyles.Any) }, { typeof(BigInteger[]).FullName, a => getParamterArrayValue(typeof(BigInteger), a, 0) }, { typeof(BigInteger?[]).FullName, a => getParamterArrayValue(typeof(BigInteger?), a, null) }, + + + { typeof((IPAddress Address, int Subnet)).FullName, a => { + var inet = ((IPAddress Address, int Subnet))a; + if (inet.Address == null) return (IPAddress.Any, inet.Subnet); + return inet; + } }, + { typeof((IPAddress Address, int Subnet)[]).FullName, a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)), a, (IPAddress.Any, 0)) }, + { typeof((IPAddress Address, int Subnet)?[]).FullName, a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)?), a, null) }, + }; + static object getParamterValue(Type type, object value, int level = 0) + { + if (type.FullName == "System.Byte[]") return value; + if (type.FullName == "System.Char[]") return value; + if (type.IsArray && level == 0) + { + var elementType = type.GetElementType(); + Type enumType = null; + if (elementType.IsEnum) enumType = elementType; + else if (elementType.IsNullableType() && elementType.GenericTypeArguments.First().IsEnum) enumType = elementType.GenericTypeArguments.First(); + if (enumType != null) return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ? + getParamterArrayValue(typeof(long), value, elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue()) : + getParamterArrayValue(typeof(int), value, elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue()); + return dicGetParamterValue.TryGetValue(type.FullName, out var trydicarr) ? trydicarr(value) : value; + } + if (type.IsNullableType()) type = type.GenericTypeArguments.First(); + if (type.IsEnum) return (int)value; + if (dicGetParamterValue.TryGetValue(type.FullName, out var trydic)) return trydic(value); + return value; + } + + public override DbParameter AppendParamter(List _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 XGParameters { ParameterName = QuoteParamterName(parameterName), Value = value }; + + var tp = _orm.CodeFirst.GetDbInfo(type)?.type; + if (col != null) + { + var dbtype = (XGDbType)_orm.DbFirst.GetDbType(new DatabaseModel.DbColumnInfo { DbTypeText = col.DbTypeText }); + if (dbtype != null) + { + 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(sql, obj, "@", (name, type, value) => + { + if (value != null) value = getParamterValue(type, value); + var ret = new XGParameters { ParameterName = $"@{name}", Value = value }; + + var tp = _orm.CodeFirst.GetDbInfo(type)?.type; + + return ret; + }); + + public override string FormatSql(string sql, params object[] args) => sql?.FormatXuguSQL(args); + + public override string TrimQuoteSqlName(string name) + { + var nametrim = name.Trim(); + if (nametrim.StartsWith("(") && nametrim.EndsWith(")")) + return nametrim; //原生SQL + return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}"; + } + public override string[] SplitTableName(string name) => GetSplitTableNames(name, '"', '"', 2); + public override string QuoteParamterName(string name) => $"@{name}"; + public override string IsNull(string sql, object value) => $"coalesce({sql}, {value})"; + public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}"; + public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}"; + public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}"; + public override string Now => "current_timestamp"; + public override string NowUtc => "(current_timestamp at time zone 'UTC')"; + + public override string QuoteWriteParamterAdapter(Type type, string paramterName) => paramterName; + protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName) => columnName; + + static ConcurrentDictionary _dicIsAssignableFromPostgisGeometry = new ConcurrentDictionary(); + public override string GetNoneParamaterSqlValue(List specialParams, string specialParamFlag, ColumnInfo col, Type type, object value) + { + if (value == null) return "NULL"; + if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value); + + value = getParamterValue(type, value); + var type2 = value.GetType(); + if (type2 == typeof(byte[])) return $"'\\x{CommonUtils.BytesSqlRaw(value as byte[])}'"; + if (type2 == typeof(TimeSpan) || type2 == typeof(TimeSpan?)) + { + var ts = (TimeSpan)value; + return $"'{Math.Min(24, (int)Math.Floor(ts.TotalHours))}:{ts.Minutes}:{ts.Seconds}'"; + } + else if (value is Array) + { + var valueArr = value as Array; + var eleType = type2.GetElementType(); + var len = valueArr.GetLength(0); + var sb = new StringBuilder().Append("ARRAY["); + for (var a = 0; a < len; a++) + { + var item = valueArr.GetValue(a); + if (a > 0) sb.Append(","); + sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, eleType, item)); + } + sb.Append("]"); + var dbinfo = _orm.CodeFirst.GetDbInfo(type); + if (dbinfo != null) sb.Append("::").Append(dbinfo.dbtype); + return sb.ToString(); + } + else if (type2 == typeof(BitArray)) + { + return $"'{(value as BitArray).To1010()}'"; + } + else if (dicGetParamterValue.ContainsKey(type2.FullName)) + { + value = string.Concat(value); + } + return FormatSql("{0}", value, 1); + } + } +} diff --git a/Providers/FreeSql.Provider.Xugu/key.snk b/Providers/FreeSql.Provider.Xugu/key.snk new file mode 100644 index 00000000..be114935 Binary files /dev/null and b/Providers/FreeSql.Provider.Xugu/key.snk differ