diff --git a/FreeSql.Tests/PostgreSQL/PostgreSQLDbFirstTest.cs b/FreeSql.Tests/PostgreSQL/PostgreSQLDbFirstTest.cs
new file mode 100644
index 00000000..52aa48bc
--- /dev/null
+++ b/FreeSql.Tests/PostgreSQL/PostgreSQLDbFirstTest.cs
@@ -0,0 +1,21 @@
+using FreeSql.DataAnnotations;
+using System;
+using Xunit;
+
+namespace FreeSql.Tests.PostgreSQL {
+ public class PostgreSQLDbFirstTest {
+ [Fact]
+ public void GetDatabases() {
+
+ var t1 = g.pgsql.DbFirst.GetDatabases();
+
+ }
+
+ [Fact]
+ public void GetTablesByDatabase() {
+
+ var t2 = g.pgsql.DbFirst.GetTablesByDatabase(g.pgsql.DbFirst.GetDatabases()[1]);
+
+ }
+ }
+}
diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
new file mode 100644
index 00000000..c17fb21c
--- /dev/null
+++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
@@ -0,0 +1,54 @@
+using NpgsqlTypes;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Reflection;
+
+public static class FreeSqlGlobalExtensions {
+
+ ///
+ /// 测量两个经纬度的距离,返回单位:米
+ ///
+ /// 经纬坐标1
+ /// 经纬坐标2
+ /// 返回距离(单位:米)
+ public static double Distance(this Point that, Point point) {
+ double radLat1 = (double)(that.Y) * Math.PI / 180d;
+ double radLng1 = (double)(that.X) * Math.PI / 180d;
+ double radLat2 = (double)(point.Y) * Math.PI / 180d;
+ double radLng2 = (double)(point.X) * Math.PI / 180d;
+ return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
+ }
+
+ public static object GetEnum(this IDataReader dr, int index) {
+ string value = dr.GetString(index);
+ Type t = typeof(T);
+ foreach (var f in t.GetFields())
+ if (f.GetCustomAttribute()?.Description == value || f.Name == value) return Enum.Parse(t, f.Name, true);
+ return null;
+ }
+
+ public static string ToDescriptionOrString(this Enum item) {
+ string name = item.ToString();
+ var desc = item.GetType().GetField(name)?.GetCustomAttribute();
+ return desc?.Description ?? name;
+ }
+ public static long ToInt64(this Enum item) {
+ return Convert.ToInt64(item);
+ }
+ public static IEnumerable ToSet(this long value) {
+ List ret = new List();
+ if (value == 0) return ret;
+ Type t = typeof(T);
+ foreach (FieldInfo f in t.GetFields()) {
+ if (f.FieldType != t) continue;
+ object o = Enum.Parse(t, f.Name, true);
+ long v = (long)o;
+ if ((value & v) == v) ret.Add((T)o);
+ }
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/FreeSql/Extensions/StringExtensions.cs b/FreeSql/Extensions/FreeSqlStringExtensions.cs
similarity index 74%
rename from FreeSql/Extensions/StringExtensions.cs
rename to FreeSql/Extensions/FreeSqlStringExtensions.cs
index a43aecee..05612322 100644
--- a/FreeSql/Extensions/StringExtensions.cs
+++ b/FreeSql/Extensions/FreeSqlStringExtensions.cs
@@ -1,4 +1,4 @@
-public static class StringExtensions {
+public static class FreeSqlStringExtensions {
///
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
@@ -24,6 +24,14 @@
///
public static string FormatPostgreSQL(this string that, params object[] args) => _postgresqlAdo.Addslashes(that, args);
static FreeSql.PostgreSQL.PostgreSQLAdo _postgresqlAdo = new FreeSql.PostgreSQL.PostgreSQLAdo();
+ ///
+ /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
+ ///
+ ///
+ ///
+ ///
+ public static string FormatOracleSQL(this string that, params object[] args) => _oracleAdo.Addslashes(that, args);
+ static FreeSql.Oracle.OracleAdo _oracleAdo = new FreeSql.Oracle.OracleAdo();
}
namespace System.Runtime.CompilerServices {
diff --git a/FreeSql/Extensions/NpgsqlTypesExtensions.cs b/FreeSql/Extensions/NpgsqlTypesExtensions.cs
deleted file mode 100644
index 2ca57bf7..00000000
--- a/FreeSql/Extensions/NpgsqlTypesExtensions.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using NpgsqlTypes;
-using System;
-using System.Collections;
-
-namespace NpgsqlTypes {
- public static class FreeSqlExtensions {
-
- 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;
- }
-
- public static NpgsqlRange ToNpgsqlRange(this string that) {
- var s = that;
- if (string.IsNullOrEmpty(s) || s == "empty") return NpgsqlRange.Empty;
- string s1 = s.Trim('(', ')', '[', ']');
- string[] ss = s1.Split(new char[] { ',' }, 2);
- if (ss.Length != 2) return NpgsqlRange.Empty;
- T t1 = default(T);
- T t2 = default(T);
- if (!string.IsNullOrEmpty(ss[0])) t1 = (T)Convert.ChangeType(ss[0], typeof(T));
- if (!string.IsNullOrEmpty(ss[1])) t2 = (T)Convert.ChangeType(ss[1], typeof(T));
- return new NpgsqlRange(t1, s[0] == '[', s[0] == '(', t2, s[s.Length - 1] == ']', s[s.Length - 1] == ')');
- }
- }
-}
\ No newline at end of file
diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj
index f8b3a13f..05739388 100644
--- a/FreeSql/FreeSql.csproj
+++ b/FreeSql/FreeSql.csproj
@@ -20,6 +20,7 @@
+
diff --git a/FreeSql/FreeSqlBuilder.cs b/FreeSql/FreeSqlBuilder.cs
index 299d30e9..d9cde2e6 100644
--- a/FreeSql/FreeSqlBuilder.cs
+++ b/FreeSql/FreeSqlBuilder.cs
@@ -92,6 +92,7 @@ namespace FreeSql {
case DataType.MySql: ret = new MySql.MySqlProvider(_cache, _logger, _masterConnectionString, _slaveConnectionString); break;
case DataType.SqlServer: ret = new SqlServer.SqlServerProvider(_cache, _logger, _masterConnectionString, _slaveConnectionString); break;
case DataType.PostgreSQL: ret = new PostgreSQL.PostgreSQLProvider(_cache, _logger, _masterConnectionString, _slaveConnectionString); break;
+ //case DataType.Oracle: ret = new Oracle.OracleProvider(_cache, _logger, _masterConnectionString, _slaveConnectionString); break;
}
if (ret != null) {
ret.CodeFirst.IsAutoSyncStructure = _isAutoSyncStructure;
@@ -104,5 +105,5 @@ namespace FreeSql {
}
}
- public enum DataType { MySql, SqlServer, PostgreSQL }
+ public enum DataType { MySql, SqlServer, PostgreSQL, /*Oracle*/ }
}
diff --git a/FreeSql/Internal/CommonProvider/InsertProvider.cs b/FreeSql/Internal/CommonProvider/InsertProvider.cs
index c2f6cece..454aaa7f 100644
--- a/FreeSql/Internal/CommonProvider/InsertProvider.cs
+++ b/FreeSql/Internal/CommonProvider/InsertProvider.cs
@@ -58,7 +58,7 @@ namespace FreeSql.Internal.CommonProvider {
return this;
}
- public string ToSql() {
+ public virtual string ToSql() {
if (_source == null || _source.Any() == false) return null;
var sb = new StringBuilder();
sb.Append("INSERT INTO ").Append(_commonUtils.QuoteSqlName(_table.DbName)).Append("(");
diff --git a/FreeSql/MySql/MySqlAdo/MygisTypesExtensions.cs b/FreeSql/MySql/MySqlAdo/MygisTypesExtensions.cs
index c8f68ff5..5047e62e 100644
--- a/FreeSql/MySql/MySqlAdo/MygisTypesExtensions.cs
+++ b/FreeSql/MySql/MySqlAdo/MygisTypesExtensions.cs
@@ -20,47 +20,4 @@ public static partial class MygisTypesExtensions {
double radLng2 = (double)(point.X) * Math.PI / 180d;
return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
}
-
- ///
- /// 测量两个经纬度的距离,返回单位:米
- ///
- /// 经纬坐标1
- /// 经纬坐标2
- /// 返回距离(单位:米)
- public static double Distance(this Point that, Point point) {
- double radLat1 = (double)(that.Y) * Math.PI / 180d;
- double radLng1 = (double)(that.X) * Math.PI / 180d;
- double radLat2 = (double)(point.Y) * Math.PI / 180d;
- double radLng2 = (double)(point.X) * Math.PI / 180d;
- return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
- }
-
- public static object GetEnum(this IDataReader dr, int index) {
- string value = dr.GetString(index);
- Type t = typeof(T);
- foreach (var f in t.GetFields())
- if (f.GetCustomAttribute()?.Description == value || f.Name == value) return Enum.Parse(t, f.Name, true);
- return null;
- }
-
- public static string ToDescriptionOrString(this Enum item) {
- string name = item.ToString();
- var desc = item.GetType().GetField(name)?.GetCustomAttribute();
- return desc?.Description ?? name;
- }
- public static long ToInt64(this Enum item) {
- return Convert.ToInt64(item);
- }
- public static IEnumerable ToSet(this long value) {
- List ret = new List();
- if (value == 0) return ret;
- Type t = typeof(T);
- foreach (FieldInfo f in t.GetFields()) {
- if (f.FieldType != t) continue;
- object o = Enum.Parse(t, f.Name, true);
- long v = (long)o;
- if ((value & v) == v) ret.Add((T)o);
- }
- return ret;
- }
}
\ No newline at end of file
diff --git a/FreeSql/MySql/MySqlCodeFirst.cs b/FreeSql/MySql/MySqlCodeFirst.cs
index 96d8024e..6fe268db 100644
--- a/FreeSql/MySql/MySqlCodeFirst.cs
+++ b/FreeSql/MySql/MySqlCodeFirst.cs
@@ -85,7 +85,7 @@ namespace FreeSql.MySql {
var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
var database = conn.Value.Database;
Func ExecuteScalar = (db, sql) => {
- if (string.Compare(database, db) != 0) try { conn.Value.ChangeDatabase(db); } catch { }
+ if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
try {
using (var cmd = conn.Value.CreateCommand()) {
cmd.CommandText = sql;
@@ -107,16 +107,16 @@ namespace FreeSql.MySql {
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] };
- if (string.Compare(tbname[0], database, true) != 0 && ExecuteScalar(database, $"select 1 from pg_database where datname='{tbname[0]}'") == null) //创建数据库
+ if (string.Compare(tbname[0], database, true) != 0 && ExecuteScalar(database, " select 1 from pg_database where datname={0}".FormatMySql(tbname[0])) == null) //创建数据库
sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(" default charset utf8 COLLATE utf8_general_ci;\r\n");
var sbalter = new StringBuilder();
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
- if (ExecuteScalar(tbname[0], "SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}".FormatMySql(tbname)) == null) { //表不存在
+ if (ExecuteScalar(tbname[0], " SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}".FormatMySql(tbname)) == null) { //表不存在
if (tboldname != null) {
- if (string.Compare(tboldname[0], tbname[0], true) != 0 && ExecuteScalar(database, $"select 1 from information_schema.schemata where schema_name='{tboldname[0]}'") == null ||
- ExecuteScalar(tboldname[0], "SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}".FormatMySql(tboldname)) == null)
- //数据库或模式或表不存在
+ if (string.Compare(tboldname[0], tbname[0], true) != 0 && ExecuteScalar(database, " select 1 from information_schema.schemata where schema_name={0}".FormatMySql(tboldname[0])) == null ||
+ ExecuteScalar(tboldname[0], " SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}".FormatMySql(tboldname)) == null)
+ //数据库或表不存在
tboldname = null;
}
if (tboldname == null) {
@@ -149,11 +149,8 @@ namespace FreeSql.MySql {
tboldname = null; //如果新表已经存在,不走改表名逻辑
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
- var addcols = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
- foreach (var tbcol in tb.Columns) addcols.Add(tbcol.Value.Attribute.Name, tbcol.Value);
- var surplus = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
- var dbcols = new List();
- var sql = @"select
+ var sql = @"
+select
a.column_name,
a.column_type,
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
diff --git a/FreeSql/MySql/MySqlDbFirst.cs b/FreeSql/MySql/MySqlDbFirst.cs
index 5221bed1..d9cc5442 100644
--- a/FreeSql/MySql/MySqlDbFirst.cs
+++ b/FreeSql/MySql/MySqlDbFirst.cs
@@ -116,8 +116,8 @@ namespace FreeSql.MySql {
{ (int)MySqlDbType.VarString, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
{ (int)MySqlDbType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
- { (int)MySqlDbType.Set, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Set", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
- { (int)MySqlDbType.Enum, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Enum", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
+ { (int)MySqlDbType.Set, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Set", typeof(Enum), typeof(Enum), "{0}", "GetInt64") },
+ { (int)MySqlDbType.Enum, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Enum", typeof(Enum), typeof(Enum), "{0}", "GetInt64") },
{ (int)MySqlDbType.Geometry, ("(MygisGeometry)", "MygisGeometry.Parse({0}.Replace(StringifySplit, \"|\"))", "{0}.AsText().Replace(\"|\", StringifySplit)", "MygisGeometry", typeof(MygisGeometry), typeof(MygisGeometry), "{0}", "GetString") },
};
@@ -131,7 +131,7 @@ namespace FreeSql.MySql {
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
public List GetDatabases() {
- var sql = @"select schema_name from information_schema.schemata where schema_name not in ('information_schema', 'mysql', 'performance_schema')";
+ var sql = @" select schema_name from information_schema.schemata where schema_name not in ('information_schema', 'mysql', 'performance_schema')";
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
}
@@ -143,7 +143,8 @@ namespace FreeSql.MySql {
if (database == null || database.Any() == false) return loc1;
var databaseIn = string.Join(",", database.Select(a => "{0}".FormatMySql(a)));
- var sql = string.Format(@"select
+ var sql = string.Format(@"
+select
concat(a.table_schema, '.', a.table_name) 'id',
a.table_schema 'schema',
a.table_name 'table',
@@ -180,7 +181,8 @@ where a.table_schema in ({0})", databaseIn);
var loc8 = "'" + string.Join("','", loc6.ToArray()) + "'";
var loc88 = "'" + string.Join("','", loc66.ToArray()) + "'";
- sql = string.Format(@"select
+ sql = string.Format(@"
+select
concat(a.table_schema, '.', a.table_name),
a.column_name,
a.data_type,
@@ -225,7 +227,8 @@ where a.table_schema in ({1}) and a.table_name in ({0})
loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
}
- sql = string.Format(@"select
+ sql = string.Format(@"
+select
concat(a.constraint_schema, '.', a.table_name) 'table_id',
a.column_name,
concat(a.constraint_schema, '/', a.table_name, '/', a.constraint_name) 'index_id',
@@ -282,7 +285,8 @@ where a.constraint_schema in ({1}) and a.table_name in ({0}) and isnull(position
}
}
- sql = string.Format(@"select
+ sql = string.Format(@"
+select
concat(a.constraint_schema, '.', a.table_name) 'table_id',
a.column_name,
concat(a.constraint_schema, '/', a.constraint_name) 'FKId',
diff --git a/FreeSql/MySql/MySqlUtils.cs b/FreeSql/MySql/MySqlUtils.cs
index f551694a..99add13c 100644
--- a/FreeSql/MySql/MySqlUtils.cs
+++ b/FreeSql/MySql/MySqlUtils.cs
@@ -9,8 +9,8 @@ namespace FreeSql.MySql {
class MySqlUtils : CommonUtils {
IFreeSql _orm;
- public MySqlUtils(IFreeSql mysql) {
- _orm = mysql;
+ public MySqlUtils(IFreeSql orm) {
+ _orm = orm;
}
internal override DbParameter AppendParamter(List _params, string parameterName, Type type, object value) {
diff --git a/FreeSql/Oracle/Curd/OracleDelete.cs b/FreeSql/Oracle/Curd/OracleDelete.cs
new file mode 100644
index 00000000..90b0b61a
--- /dev/null
+++ b/FreeSql/Oracle/Curd/OracleDelete.cs
@@ -0,0 +1,45 @@
+using FreeSql.Internal;
+using System.Collections.Generic;
+using System.Data;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FreeSql.Oracle.Curd {
+
+ class OracleDelete : Internal.CommonProvider.DeleteProvider where T1 : class {
+ public OracleDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
+ : base(orm, commonUtils, commonExpression, dywhere) {
+ }
+
+ public override List ExecuteDeleted() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return new List();
+
+ var sb = new StringBuilder();
+ sb.Append(sql).Append(" RETURNING ");
+
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
+ ++colidx;
+ }
+ return _orm.Ado.Query(CommandType.Text, sb.ToString(), _params.ToArray());
+ }
+ async public override Task> ExecuteDeletedAsync() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return new List();
+
+ var sb = new StringBuilder();
+ sb.Append(sql).Append(" RETURNING ");
+
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
+ ++colidx;
+ }
+ return await _orm.Ado.QueryAsync(CommandType.Text, sb.ToString(), _params.ToArray());
+ }
+ }
+}
diff --git a/FreeSql/Oracle/Curd/OracleInsert.cs b/FreeSql/Oracle/Curd/OracleInsert.cs
new file mode 100644
index 00000000..af8ac146
--- /dev/null
+++ b/FreeSql/Oracle/Curd/OracleInsert.cs
@@ -0,0 +1,105 @@
+using FreeSql.Internal;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FreeSql.Oracle.Curd {
+
+ class OracleInsert : Internal.CommonProvider.InsertProvider where T1 : class {
+ public OracleInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
+ : base(orm, commonUtils, commonExpression) {
+ }
+
+ public override string ToSql() {
+ if (_source == null || _source.Any() == false) return null;
+ var sb = new StringBuilder();
+ sb.Append("INSERT INTO ").Append(_commonUtils.QuoteSqlName(_table.DbName)).Append("(");
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values)
+ if (_ignore.ContainsKey(col.Attribute.Name) == false) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name));
+ ++colidx;
+ }
+ sb.Append(") VALUES");
+ _params = new DbParameter[colidx * _source.Count];
+ var didx = 0;
+ foreach (var d in _source) {
+ if (didx > 0) sb.Append(", ");
+ sb.Append("(");
+ var colidx2 = 0;
+ foreach (var col in _table.Columns.Values)
+ if (_ignore.ContainsKey(col.Attribute.Name) == false) {
+ if (colidx2 > 0) sb.Append(", ");
+ if (col.Attribute.IsIdentity) {
+ sb.Append(_commonUtils.QuoteSqlName($"{Utils.GetCsName(_table.DbName)}_seq_{col.Attribute.Name}")).Append(".nextval");
+ } else {
+ sb.Append(_commonUtils.QuoteWriteParamter(col.CsType, $"{_commonUtils.QuoteParamterName(col.CsName)}{didx}"));
+ _params[didx * colidx + colidx2] = _commonUtils.AppendParamter(null, $"{col.CsName}{didx}", col.CsType, _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : null);
+ }
+ ++colidx2;
+ }
+ sb.Append(")");
+ ++didx;
+ }
+ return sb.ToString();
+ }
+
+ public override long ExecuteIdentity() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return 0;
+
+ var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity);
+ if (identCols.Any() == false) {
+ _orm.Ado.ExecuteNonQuery(CommandType.Text, sql, _params);
+ return 0;
+ }
+ return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
+ }
+ async public override Task ExecuteIdentityAsync() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return 0;
+
+ var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity);
+ if (identCols.Any() == false) {
+ await _orm.Ado.ExecuteNonQueryAsync(CommandType.Text, sql, _params);
+ return 0;
+ }
+ return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
+ }
+
+ public override List ExecuteInserted() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return new List();
+
+ var sb = new StringBuilder();
+ sb.Append(sql).Append(" RETURNING ");
+
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
+ ++colidx;
+ }
+ return _orm.Ado.Query(CommandType.Text, sb.ToString(), _params);
+ }
+ async public override Task> ExecuteInsertedAsync() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return new List();
+
+ var sb = new StringBuilder();
+ sb.Append(sql).Append(" RETURNING ");
+
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
+ ++colidx;
+ }
+ return await _orm.Ado.QueryAsync(CommandType.Text, sb.ToString(), _params);
+ }
+ }
+}
diff --git a/FreeSql/Oracle/Curd/OracleSelect.cs b/FreeSql/Oracle/Curd/OracleSelect.cs
new file mode 100644
index 00000000..bc5050d3
--- /dev/null
+++ b/FreeSql/Oracle/Curd/OracleSelect.cs
@@ -0,0 +1,126 @@
+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.Oracle.Curd {
+
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select1Provider where T1 : class {
+
+ internal static string ToSqlStatic(CommonUtils _commonUtils, string _select, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List _tables, IFreeSql _orm) {
+ if (_orm.CodeFirst.IsAutoSyncStructure)
+ _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
+
+ var sb = new StringBuilder();
+ sb.Append(_select);
+ sb.Append(field);
+ if (string.IsNullOrEmpty(_orderby) == false && (_skip > 0 || _limit > 0)) sb.Append(", ROWNUM AS __rownum__");
+ sb.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(tbsfrom[a].Table.DbName)).Append(" ").Append(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(tbsfrom[b].Table.DbName)).Append(" ").Append(tbsfrom[b].Alias).Append(" ON 1 = 1");
+ break;
+ }
+ if (a < tbsfrom.Length - 1) sb.Append(", ");
+ }
+ foreach (var tb in tbsjoin) {
+ if (tb.Type == SelectTableInfoType.Parent) continue;
+ switch (tb.Type) {
+ 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(tb.Table.DbName)).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On);
+ }
+ if (_join.Length > 0) sb.Append(_join);
+
+ var sbqf = new StringBuilder();
+ foreach (var tb in _tables) {
+ if (tb.Type == SelectTableInfoType.Parent) continue;
+ if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
+ sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
+ }
+ if (_where.Length > 0) {
+ sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5));
+ if (string.IsNullOrEmpty(_orderby) == false && (_skip > 0 || _limit > 0)) sb.Append(" AND ROWNUM < ").Append(_skip + _limit + 1);
+ if (sbqf.Length > 0) sb.Append(sbqf.ToString());
+ } else {
+ if (string.IsNullOrEmpty(_orderby) == false && (_skip > 0 || _limit > 0)) {
+ sb.Append(" \r\nWHERE ROWNUM < ").Append(_skip + _limit + 1);
+ if (sbqf.Length > 0) sb.Append(sbqf.Remove(0, 5));
+ }
+ else if (sbqf.Length > 0) sb.Append(" \r\nWHERE ").Append(sbqf.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 (string.IsNullOrEmpty(_orderby) == false) {
+ if (_skip > 0 && _limit > 0) sb.Insert(0, "SELECT t.* FROM (SELECT ROWNUM AS rt.*, __rownum__ FROM (").Append(") rt WHERE ROWNUM < ").Append(_skip + _limit + 1).Append(") t WHERE t.__rownum__ > ").Append(_skip);
+ else if (_skip > 0 || _limit > 0) sb.Insert(0, "SELECT t.* FROM (").Append(") t WHERE ROWNUM < ").Append(_skip + _limit + 1);
+ } else if (_skip > 0)
+ sb.Insert(0, "SELECT t.* FROM (").Append(") t WHERE t.__rownum__ > ").Append(_skip);
+
+ return sb.ToString();
+ }
+
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect(_orm, _commonUtils, _commonExpression, null); OracleSelect.CopyData(this, ret); return ret; }
+ public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select3Provider where T1 : class where T2 : class where T3 : class {
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select4Provider where T1 : class where T2 : class where T3 : class where T4 : class {
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select5Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class {
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select6Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class {
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select7Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class {
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select8Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class {
+ public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select9Provider where T1 : class 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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+ class OracleSelect : FreeSql.Internal.CommonProvider.Select10Provider where T1 : class 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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
+ public override string ToSql(string field = null) => OracleSelect.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, _orm);
+ }
+}
diff --git a/FreeSql/Oracle/Curd/OracleUpdate.cs b/FreeSql/Oracle/Curd/OracleUpdate.cs
new file mode 100644
index 00000000..8e7d6d3d
--- /dev/null
+++ b/FreeSql/Oracle/Curd/OracleUpdate.cs
@@ -0,0 +1,78 @@
+using FreeSql.Internal;
+using FreeSql.Internal.Model;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FreeSql.Oracle.Curd {
+
+ class OracleUpdate : Internal.CommonProvider.UpdateProvider where T1 : class {
+
+ public OracleUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
+ : base(orm, commonUtils, commonExpression, dywhere) {
+ }
+
+ public override List ExecuteUpdated() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return new List();
+
+ var sb = new StringBuilder();
+ sb.Append(sql).Append(" RETURNING ");
+
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
+ ++colidx;
+ }
+ return _orm.Ado.Query(CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray());
+ }
+ async public override Task> ExecuteUpdatedAsync() {
+ var sql = this.ToSql();
+ if (string.IsNullOrEmpty(sql)) return new List();
+
+ var sb = new StringBuilder();
+ sb.Append(sql).Append(" RETURNING ");
+
+ var colidx = 0;
+ foreach (var col in _table.Columns.Values) {
+ if (colidx > 0) sb.Append(", ");
+ sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
+ ++colidx;
+ }
+ return await _orm.Ado.QueryAsync(CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray());
+ }
+
+ protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
+ if (_table.Primarys.Length == 1) {
+ caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().CsType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
+ return;
+ }
+ caseWhen.Append("(");
+ var pkidx = 0;
+ foreach (var pk in _table.Primarys) {
+ if (pkidx > 0) caseWhen.Append(" || ");
+ caseWhen.Append(_commonUtils.QuoteReadColumn(pk.CsType, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
+ ++pkidx;
+ }
+ caseWhen.Append(")");
+ }
+
+ protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
+ if (_table.Primarys.Length == 1) {
+ sb.Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(_table.Primarys.First().CsName, out var tryp2) ? tryp2.GetValue(d) : null));
+ return;
+ }
+ sb.Append("(");
+ var pkidx = 0;
+ foreach (var pk in _table.Primarys) {
+ if (pkidx > 0) sb.Append(" || ");
+ sb.Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(pk.CsName, out var tryp2) ? tryp2.GetValue(d) : null));
+ ++pkidx;
+ }
+ sb.Append(")");
+ }
+ }
+}
diff --git a/FreeSql/Oracle/OracleAdo/OracleAdo.cs b/FreeSql/Oracle/OracleAdo/OracleAdo.cs
new file mode 100644
index 00000000..0aee69f9
--- /dev/null
+++ b/FreeSql/Oracle/OracleAdo/OracleAdo.cs
@@ -0,0 +1,65 @@
+using FreeSql.Internal;
+using Microsoft.Extensions.Logging;
+using Oracle.ManagedDataAccess.Client;
+using SafeObjectPool;
+using System;
+using System.Collections;
+using System.Data.Common;
+using System.Text;
+using System.Threading;
+
+namespace FreeSql.Oracle {
+ class OracleAdo : FreeSql.Internal.CommonProvider.AdoProvider {
+ CommonUtils _util;
+
+ public OracleAdo() : base(null, null) { }
+ public OracleAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log) {
+ this._util = util;
+ MasterPool = new OracleConnectionPool("主库", masterConnectionString, null, null);
+ if (slaveConnectionStrings != null) {
+ foreach (var slaveConnectionString in slaveConnectionStrings) {
+ var slavePool = new OracleConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
+ SlavePools.Add(slavePool);
+ }
+ }
+ }
+ static DateTime dt1970 = new DateTime(1970, 1, 1);
+ public override object AddslashesProcessParam(object param) {
+ if (param == null) return "NULL";
+ if (param is bool || param is bool?)
+ return (bool)param ? 1 : 0;
+ else if (param is string)
+ return string.Concat("'", param.ToString().Replace("'", "''"), "'");
+ 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)
+ return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss"), "'");
+ else if (param is DateTime?)
+ return string.Concat("'", (param as DateTime?).Value.ToString("yyyy-MM-dd HH:mm:ss"), "'");
+ else if (param is TimeSpan)
+ return ((TimeSpan)param).Ticks / 10;
+ else if (param is TimeSpan?)
+ return (param as TimeSpan?).Value.Ticks / 10;
+ else if (param is IEnumerable) {
+ var sb = new StringBuilder();
+ var ie = param as IEnumerable;
+ foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z));
+ return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
+ }
+ return string.Concat("'", param.ToString().Replace("'", "''"), "'");
+ //if (param is string) return string.Concat('N', nparms[a]);
+ }
+
+ protected override DbCommand CreateCommand() {
+ return new OracleCommand();
+ }
+
+ protected override void ReturnConnection(ObjectPool pool, Object conn, Exception ex) {
+ (pool as OracleConnectionPool).Return(conn, ex);
+ }
+
+ protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
+ }
+}
diff --git a/FreeSql/Oracle/OracleAdo/OracleConnectionPool.cs b/FreeSql/Oracle/OracleAdo/OracleConnectionPool.cs
new file mode 100644
index 00000000..6d973d22
--- /dev/null
+++ b/FreeSql/Oracle/OracleAdo/OracleConnectionPool.cs
@@ -0,0 +1,153 @@
+using Oracle.ManagedDataAccess.Client;
+using SafeObjectPool;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace FreeSql.Oracle {
+
+ public class OracleConnectionPool : ObjectPool {
+
+ internal Action availableHandler;
+ internal Action unavailableHandler;
+
+ public OracleConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) {
+ var policy = new OracleConnectionPoolPolicy {
+ _pool = this,
+ Name = name
+ };
+ this.Policy = policy;
+ policy.ConnectionString = connectionString;
+
+ this.availableHandler = availableHandler;
+ this.unavailableHandler = unavailableHandler;
+ }
+
+ public void Return(Object obj, Exception exception, bool isRecreate = false) {
+ if (exception != null && exception is OracleException) {
+
+ if (exception is System.IO.IOException) {
+
+ base.SetUnavailable(exception);
+
+ } else if (obj.Value.Ping() == false) {
+
+ base.SetUnavailable(exception);
+ }
+ }
+ base.Return(obj, isRecreate);
+ }
+ }
+
+ public class OracleConnectionPoolPolicy : IPolicy {
+
+ internal OracleConnectionPool _pool;
+ public string Name { get; set; } = "Oracle Connection 对象池";
+ public int PoolSize { get; set; } = 100;
+ public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
+ public int AsyncGetCapacity { get; set; } = 10000;
+ public bool IsThrowGetTimeoutException { get; set; } = true;
+ public int CheckAvailableInterval { get; set; } = 5;
+
+ private string _connectionString;
+ public string ConnectionString {
+ get => _connectionString;
+ set {
+ _connectionString = value ?? "";
+ Match m = Regex.Match(_connectionString, @"Max\s*pool\s*size\s*=\s*(\d+)", RegexOptions.IgnoreCase);
+ if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
+ PoolSize = poolsize;
+
+ var initConns = new Object[poolsize];
+ for (var a = 0; a < poolsize; a++) try { initConns[a] = _pool.Get(); } catch { }
+ foreach (var conn in initConns) _pool.Return(conn);
+ }
+ }
+
+
+ public bool OnCheckAvailable(Object obj) {
+ if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
+ var cmd = obj.Value.CreateCommand();
+ cmd.CommandText = "select 1";
+ cmd.ExecuteNonQuery();
+ return true;
+ }
+
+ public DbConnection OnCreate() {
+ var conn = new OracleConnection(_connectionString);
+ return conn;
+ }
+
+ public void OnDestroy(DbConnection obj) {
+ if (obj.State != ConnectionState.Closed) obj.Close();
+ obj.Dispose();
+ }
+
+ public void OnGet(Object obj) {
+
+ if (_pool.IsAvailable) {
+
+ 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) == true)
+ throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
+ }
+ }
+ }
+ }
+
+ async public Task OnGetAsync(Object obj) {
+
+ if (_pool.IsAvailable) {
+
+ if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) {
+
+ try {
+ await obj.Value.OpenAsync();
+ } catch (Exception ex) {
+ if (_pool.SetUnavailable(ex) == true)
+ throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
+ }
+ }
+ }
+ }
+
+ public void OnGetTimeout() {
+
+ }
+
+ public void OnReturn(Object obj) {
+
+ }
+
+ public void OnAvailable() {
+ _pool.availableHandler?.Invoke();
+ }
+
+ public void OnUnavailable() {
+ _pool.unavailableHandler?.Invoke();
+ }
+ }
+
+ static class DbConnectionExtensions {
+
+ public static bool Ping(this DbConnection that) {
+ try {
+ var cmd = that.CreateCommand();
+ cmd.CommandText = "select 1";
+ cmd.ExecuteNonQuery();
+ return true;
+ } catch {
+ try { that.Close(); } catch { }
+ return false;
+ }
+ }
+ }
+}
diff --git a/FreeSql/Oracle/OracleCodeFirst.cs b/FreeSql/Oracle/OracleCodeFirst.cs
new file mode 100644
index 00000000..97c5bda4
--- /dev/null
+++ b/FreeSql/Oracle/OracleCodeFirst.cs
@@ -0,0 +1,297 @@
+using FreeSql.DatabaseModel;
+using FreeSql.Internal;
+using FreeSql.Internal.Model;
+using Oracle.ManagedDataAccess.Client;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace FreeSql.Oracle {
+
+ class OracleCodeFirst : ICodeFirst {
+ IFreeSql _orm;
+ protected CommonUtils _commonUtils;
+ protected CommonExpression _commonExpression;
+ public OracleCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
+ _orm = orm;
+ _commonUtils = commonUtils;
+ _commonExpression = commonExpression;
+ }
+
+ public bool IsAutoSyncStructure { get; set; } = true;
+ public bool IsSyncStructureToLower { get; set; } = false;
+
+ static object _dicCsToDbLock = new object();
+ static Dictionary _dicCsToDb = new Dictionary() {
+ { typeof(bool).FullName, (OracleDbType.Boolean, "number","number(1) NOT NULL", null, false, false) },{ typeof(bool?).FullName, (OracleDbType.Boolean, "number","number(1) NULL", null, true, null) },
+
+ { typeof(sbyte).FullName, (OracleDbType.Byte, "number", "number(4) NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (OracleDbType.Byte, "number", "number(4) NULL", false, true, null) },
+ { typeof(short).FullName, (OracleDbType.Int16, "number","number(4) NOT NULL", false, false, 0) },{ typeof(short?).FullName, (OracleDbType.Int16, "number", "number(4) NULL", false, true, null) },
+ { typeof(int).FullName, (OracleDbType.Int32, "number", "number(8) NOT NULL", false, false, 0) },{ typeof(int?).FullName, (OracleDbType.Int32, "number", "number(8) NULL", false, true, null) },
+ { typeof(long).FullName, (OracleDbType.Int64, "number","number(16) NOT NULL", false, false, 0) },{ typeof(long?).FullName, (OracleDbType.Int64, "bigint","bigint(16) NULL", false, true, null) },
+
+ { typeof(byte).FullName, (OracleDbType.Byte, "number","number(2) NOT NULL", true, false, 0) },{ typeof(byte?).FullName, (OracleDbType.Byte, "number","number(2) NULL", true, true, null) },
+ { typeof(ushort).FullName, (OracleDbType.Int16, "number","number(8) NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, (OracleDbType.Int16, "number", "number(8) NULL", true, true, null) },
+ { typeof(uint).FullName, (OracleDbType.Int32, "number", "number(16) NOT NULL", true, false, 0) },{ typeof(uint?).FullName, (OracleDbType.Int32, "number", "number(16) NULL", true, true, null) },
+ { typeof(ulong).FullName, (OracleDbType.Int64, "number", "number(32) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, (OracleDbType.Int64, "number", "number(32) NULL", true, true, null) },
+
+ { typeof(double).FullName, (OracleDbType.Double, "double", "double(126) NOT NULL", false, false, 0) },{ typeof(double?).FullName, (OracleDbType.Double, "double", "double(126) NULL", false, true, null) },
+ { typeof(float).FullName, (OracleDbType.Single, "float","float(63) NOT NULL", false, false, 0) },{ typeof(float?).FullName, (OracleDbType.Single, "float","float(63) NULL", false, true, null) },
+ { typeof(decimal).FullName, (OracleDbType.Decimal, "number", "number(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (OracleDbType.Decimal, "number", "number(10,2) NULL", false, true, null) },
+
+ { typeof(TimeSpan).FullName, (OracleDbType.IntervalDS, "interval day to second","interval day(2) to second(6) NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (OracleDbType.IntervalDS, "interval day to second", "interval day(2) to second(6) NULL",false, true, null) },
+ { typeof(DateTime).FullName, (OracleDbType.TimeStamp, "timestamp", "timestamp(6) NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (OracleDbType.TimeStamp, "timestamp", "timestamp(6) NULL", false, true, null) },
+ { typeof(DateTimeOffset).FullName, (OracleDbType.TimeStampLTZ, "timestamp with local time zone", "timestamp(6) with local time zone NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (OracleDbType.TimeStamp, "timestamp with local time zone", "timestamp(6) with local time zone NULL", false, true, null) },
+
+ { typeof(byte[]).FullName, (OracleDbType.Blob, "blog", "blog(4000) NULL", false, null, new byte[0]) },
+ { typeof(string).FullName, (OracleDbType.NVarchar2, "nvarchar2", "nvarchar2(255) NULL", false, null, "") },
+
+ { typeof(Guid).FullName, (OracleDbType.Char, "char", "char(36 BYTE)", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (OracleDbType.Char, "char", "char(36 BYTE) NULL", false, true, null) },
+ };
+
+ public (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type) {
+ if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?, object)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue));
+ if (type.IsArray) return null;
+ var enumType = type.IsEnum ? type : null;
+ if (enumType == null && type.FullName.StartsWith("System.Nullable`1[") && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
+ if (enumType != null) {
+ var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
+ (OracleDbType.Int32, "number", $"number(16){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
+ (OracleDbType.Int64, "number", $"number(32){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
+ if (_dicCsToDb.ContainsKey(type.FullName) == false) {
+ lock (_dicCsToDbLock) {
+ if (_dicCsToDb.ContainsKey(type.FullName) == false)
+ _dicCsToDb.Add(type.FullName, newItem);
+ }
+ }
+ return ((int)newItem.Item1, newItem.Item2, newItem.Item3, newItem.Item5, newItem.Item6);
+ }
+ return null;
+ }
+
+ public string GetComparisonDDLStatements() => this.GetComparisonDDLStatements(typeof(TEntity));
+ public string GetComparisonDDLStatements(params Type[] entityTypes) {
+ var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
+ var seqcols = new List<(ColumnInfo, string[], bool)>(); //序列
+
+ var database = conn.Value.Database;
+ Func ExecuteScalar = (db, sql) => {
+ if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
+ try {
+ using (var cmd = conn.Value.CreateCommand()) {
+ cmd.CommandText = sql;
+ cmd.CommandType = CommandType.Text;
+ return cmd.ExecuteScalar();
+ }
+ } finally {
+ if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
+ }
+ };
+ var sb = new StringBuilder();
+ try {
+ foreach (var entityType in entityTypes) {
+ if (sb.Length > 0) sb.Append("\r\n");
+ var tb = _commonUtils.GetTableByEntity(entityType);
+ var tbname = tb.DbName.Split(new[] { '.' }, 2);
+ if (tbname?.Length == 1) tbname = new[] { database, tbname[0] };
+
+ var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
+ if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] };
+
+ if (string.Compare(tbname[0], database, true) != 0 && ExecuteScalar(database, " select 1 from sys.dba_users where username={0}".FormatOracleSQL(tbname[0])) == null) //创建数据库
+ throw new NotImplementedException($"Oracle CodeFirst 不支持代码创建 tablespace 与 schemas {tbname[0]}");
+
+ var sbalter = new StringBuilder();
+ var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
+ if (ExecuteScalar(tbname[0], " select 1 from all_tab_comments where owner={0} and table_name={1}".FormatOracleSQL(tbname)) == null) { //表不存在
+ if (tboldname != null) {
+ if (ExecuteScalar(tboldname[0], " select 1 from all_tab_comments where owner={0} and table_name={1}".FormatOracleSQL(tboldname)) == null)
+ //模式或表不存在
+ tboldname = null;
+ }
+ if (tboldname == null) {
+ //创建表
+ sb.Append("CREATE TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" (");
+ foreach (var tbcol in tb.Columns.Values) {
+ sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
+ if (tbcol.Attribute.IsIdentity) seqcols.Add((tbcol, tbname, true));
+ }
+ if (tb.Primarys.Any() == false)
+ sb.Remove(sb.Length - 1, 1);
+ else {
+ sb.Append(" \r\n CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pk PRIMARY KEY (");
+ foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
+ sb.Remove(sb.Length - 2, 2).Append(")");
+ }
+ sb.Append("\r\n) LOGGING \r\nNOCOMPRESS \r\nNOCACHE\r\n;\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[0]}.{tbname[1]}")).Append(";\r\n");
+ else {
+ //如果新表,旧表不在一起,创建新表,导入数据,删除旧表
+ istmpatler = true;
+ }
+ } else
+ tboldname = null; //如果新表已经存在,不走改表名逻辑
+
+ //对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
+ var addcols = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
+ foreach (var tbcol in tb.Columns) addcols.Add(tbcol.Value.Attribute.Name, tbcol.Value);
+ var surplus = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
+ var dbcols = new List();
+ var sql = $@"
+select
+column_name,
+data_type,
+data_length,
+data_precision,
+data_scale,
+char_used,
+case when nullable = 'Y' then 1 else 0 end,
+nvl((select 1 from user_sequences where sequence_name='{Utils.GetCsName(string.Join(".", tboldname ?? tbname))}_seq_'||all_tab_columns.column_name), 0)
+from all_tab_columns
+where owner={{0}} and table_name={{1}}".FormatOracleSQL(tboldname ?? tbname);
+ var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
+ var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => {
+ var sqlType = string.Concat(a[1]);
+ var data_length = long.Parse(string.Concat(a[2]));
+ long.TryParse(string.Concat(a[3]), out var data_precision);
+ long.TryParse(string.Concat(a[4]), out var data_scale);
+ var char_used = string.Concat(a[5]);
+ if (Regex.IsMatch(sqlType, @"INTERVAL DAY\(\d+\) TO SECOND\(\d+\)", RegexOptions.IgnoreCase)) {
+ } else if (Regex.IsMatch(sqlType, @"INTERVAL YEAR\(\d+\) TO MONTH", RegexOptions.IgnoreCase)) {
+ } else if (sqlType.StartsWith("TIMESTAMP", StringComparison.CurrentCultureIgnoreCase)) {
+ } else if (char_used.ToLower() == "c") {
+ sqlType += $"({data_length} CHAR)";
+ } else if (char_used.ToLower() == "b") {
+ sqlType += $"({data_length} BYTE)";
+ } else if (sqlType.ToLower() == "float") {
+ sqlType += $"({data_precision})";
+ } else if (data_precision > 0 && data_scale > 0) {
+ sqlType += $"({data_precision},{data_scale})";
+ } else {
+ sqlType += $"({data_length})";
+ }
+ return new {
+ column = string.Concat(a[0]),
+ sqlType,
+ is_nullable = string.Concat(a[6]) == "1",
+ is_identity = string.Concat(a[7]) == "1"
+ };
+ }, StringComparer.CurrentCultureIgnoreCase);
+
+ if (istmpatler == false) {
+ foreach (var tbcol in tb.Columns.Values) {
+ if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
+ string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
+ if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
+ sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY (").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.DbType).Append(");\r\n");
+ if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable) {
+ if (tbcol.Attribute.IsNullable == false)
+ sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(_commonUtils.FormatSql(" = {0}", tbcol.Attribute.DbDefautValue)).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" IS NULL;\r\n");
+ sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable ? "" : "NOT").Append(" NULL;\r\n");
+ }
+ if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
+ seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity));
+ if (tbstructcol.column == tbcol.Attribute.OldName)
+ //修改列名
+ sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" RENAME ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.OldName)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).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(_commonUtils.FormatSql(" = {0};\r\n", tbcol.Attribute.DbDefautValue));
+ if (tbcol.Attribute.IsNullable == false) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" NOT NULL;\r\n");
+ if (tbcol.Attribute.IsIdentity) seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity));
+ }
+ }
+ if (istmpatler == false) {
+ sb.Append(sbalter);
+ continue;
+ }
+
+ //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
+ 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 ").Append(tmptablename).Append(" (");
+ foreach (var tbcol in tb.Columns.Values) {
+ sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
+ if (tbcol.Attribute.IsIdentity) seqcols.Add((tbcol, tbname, true));
+ }
+ if (tb.Primarys.Any() == false)
+ sb.Remove(sb.Length - 1, 1);
+ else {
+ sb.Append(" \r\n CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pk PRIMARY KEY (");
+ foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
+ sb.Remove(sb.Length - 2, 2).Append(")");
+ }
+ sb.Append("\r\n) LOGGING \r\nNOCOMPRESS \r\nNOCACHE\r\n;\r\n");
+ sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
+ foreach (var tbcol in tb.Columns.Values)
+ sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
+ sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
+ foreach (var tbcol in tb.Columns.Values) {
+ 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 = $"nvl({insertvalue},{_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue).Replace("'", "''")})";
+ } else if (tbcol.Attribute.IsNullable == false)
+ insertvalue = _commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue).Replace("'", "''");
+ 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[0]}.{tbname[1]}")).Append(";\r\n");
+ }
+ foreach (var seqcol in seqcols) {
+ var tbname = seqcol.Item2;
+ var seqname = Utils.GetCsName($"{tbname[0]}.{tbname[1]}_seq_{seqcol.Item1.Attribute.Name}");
+ var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
+ var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name);
+ sb.Append("DROP SEQUENCE ").Append(seqname).Append(";\r\n");
+ if (seqcol.Item3) {
+ var startWith = _orm.Ado.ExecuteScalar(CommandType.Text, $" select nvl({colname2},0) from {tbname2}");
+ sb.Append("CREATE SEQUENCE ").Append(seqname).Append(" start with ").Append(startWith).Append(";\r\n");
+ }
+ }
+ return sb.Length == 0 ? null : sb.ToString();
+ } finally {
+ try {
+ conn.Value.ChangeDatabase(database);
+ _orm.Ado.MasterPool.Return(conn);
+ } catch {
+ _orm.Ado.MasterPool.Return(conn, true);
+ }
+ }
+ }
+
+ ConcurrentDictionary dicSyced = new ConcurrentDictionary();
+ public bool SyncStructure() => this.SyncStructure(typeof(TEntity));
+ public bool SyncStructure(params Type[] entityTypes) {
+ if (entityTypes == null) return true;
+ var syncTypes = entityTypes.Where(a => dicSyced.ContainsKey(a.FullName) == false).ToArray();
+ if (syncTypes.Any() == false) return true;
+ var ddl = this.GetComparisonDDLStatements(syncTypes);
+ if (string.IsNullOrEmpty(ddl)) {
+ foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
+ return true;
+ }
+ var affrows = _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl);
+ foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
+ return affrows > 0;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/FreeSql/Oracle/OracleDbFirst.cs b/FreeSql/Oracle/OracleDbFirst.cs
new file mode 100644
index 00000000..8966920c
--- /dev/null
+++ b/FreeSql/Oracle/OracleDbFirst.cs
@@ -0,0 +1,385 @@
+using FreeSql.DatabaseModel;
+using FreeSql.Internal;
+using MySql.Data.MySqlClient;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace FreeSql.Oracle {
+ class OracleDbFirst : IDbFirst {
+ IFreeSql _orm;
+ protected CommonUtils _commonUtils;
+ protected CommonExpression _commonExpression;
+ public OracleDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
+ _orm = orm;
+ _commonUtils = commonUtils;
+ _commonExpression = commonExpression;
+ }
+
+ public int GetDbType(DbColumnInfo column) => (int)GetMySqlDbType(column);
+ MySqlDbType GetMySqlDbType(DbColumnInfo column) {
+ var is_unsigned = column.DbTypeTextFull.ToLower().EndsWith(" unsigned");
+ switch (column.DbTypeText.ToLower()) {
+ case "bit": return MySqlDbType.Bit;
+
+ case "tinyint": return is_unsigned ? MySqlDbType.UByte : MySqlDbType.Byte;
+ case "smallint": return is_unsigned ? MySqlDbType.UInt16 : MySqlDbType.Int16;
+ case "mediumint": return is_unsigned ? MySqlDbType.UInt24 : MySqlDbType.Int24;
+ case "int": return is_unsigned ? MySqlDbType.UInt32 : MySqlDbType.Int32;
+ case "bigint": return is_unsigned ? MySqlDbType.UInt64 : MySqlDbType.Int64;
+
+ case "real":
+ case "double": return MySqlDbType.Double;
+ case "float": return MySqlDbType.Float;
+ case "numeric":
+ case "decimal": return MySqlDbType.Decimal;
+
+ case "year": return MySqlDbType.Year;
+ case "time": return MySqlDbType.Time;
+ case "date": return MySqlDbType.Date;
+ case "timestamp": return MySqlDbType.Timestamp;
+ case "datetime": return MySqlDbType.DateTime;
+
+ case "tinyblob": return MySqlDbType.TinyBlob;
+ case "blob": return MySqlDbType.Blob;
+ case "mediumblob": return MySqlDbType.MediumBlob;
+ case "longblob": return MySqlDbType.LongBlob;
+
+ case "binary": return MySqlDbType.Binary;
+ case "varbinary": return MySqlDbType.VarBinary;
+
+ case "tinytext": return MySqlDbType.TinyText;
+ case "text": return MySqlDbType.Text;
+ case "mediumtext": return MySqlDbType.MediumText;
+ case "longtext": return MySqlDbType.LongText;
+
+ case "char": return column.MaxLength == 36 ? MySqlDbType.Guid : MySqlDbType.String;
+ case "varchar": return MySqlDbType.VarChar;
+
+ case "set": return MySqlDbType.Set;
+ case "enum": return MySqlDbType.Enum;
+
+ case "point": return MySqlDbType.Geometry;
+ case "linestring": return MySqlDbType.Geometry;
+ case "polygon": return MySqlDbType.Geometry;
+ case "geometry": return MySqlDbType.Geometry;
+ case "multipoint": return MySqlDbType.Geometry;
+ case "multilinestring": return MySqlDbType.Geometry;
+ case "multipolygon": return MySqlDbType.Geometry;
+ case "geometrycollection": return MySqlDbType.Geometry;
+ default: return MySqlDbType.String;
+ }
+ }
+
+ static readonly Dictionary _dicDbToCs = new Dictionary() {
+ { (int)MySqlDbType.Bit, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
+
+ { (int)MySqlDbType.Byte, ("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetByte") },
+ { (int)MySqlDbType.Int16, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
+ { (int)MySqlDbType.Int24, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
+ { (int)MySqlDbType.Int32, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
+ { (int)MySqlDbType.Int64, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
+
+ { (int)MySqlDbType.UByte, ("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
+ { (int)MySqlDbType.UInt16, ("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt16") },
+ { (int)MySqlDbType.UInt24, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
+ { (int)MySqlDbType.UInt32, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
+ { (int)MySqlDbType.UInt64, ("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
+
+ { (int)MySqlDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
+ { (int)MySqlDbType.Float, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
+ { (int)MySqlDbType.Decimal, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
+
+ { (int)MySqlDbType.Year, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
+ { (int)MySqlDbType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
+ { (int)MySqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+ { (int)MySqlDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+ { (int)MySqlDbType.DateTime, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+
+ { (int)MySqlDbType.TinyBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)MySqlDbType.Blob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)MySqlDbType.MediumBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)MySqlDbType.LongBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+
+ { (int)MySqlDbType.Binary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)MySqlDbType.VarBinary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+
+ { (int)MySqlDbType.TinyText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)MySqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)MySqlDbType.MediumText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)MySqlDbType.LongText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+
+ { (int)MySqlDbType.Guid, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
+ { (int)MySqlDbType.String, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)MySqlDbType.VarString, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)MySqlDbType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+
+ { (int)MySqlDbType.Set, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Set", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
+ { (int)MySqlDbType.Enum, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Enum", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
+
+ { (int)MySqlDbType.Geometry, ("(MygisGeometry)", "MygisGeometry.Parse({0}.Replace(StringifySplit, \"|\"))", "{0}.AsText().Replace(\"|\", StringifySplit)", "MygisGeometry", typeof(MygisGeometry), typeof(MygisGeometry), "{0}", "GetString") },
+ };
+
+ 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 username from sys.dba_users";
+ var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
+ return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
+ }
+
+ public List GetTablesByDatabase(params string[] database) {
+ var loc1 = new List();
+ var loc2 = new Dictionary();
+ var loc3 = new Dictionary>();
+
+ if (database == null || database.Any() == false) return loc1;
+ var databaseIn = string.Join(",", database.Select(a => "{0}".FormatMySql(a)));
+ var sql = string.Format(@"
+select
+concat(a.table_schema, '.', a.table_name) 'id',
+a.table_schema 'schema',
+a.table_name 'table',
+a.table_type 'type'
+from information_schema.tables a
+where a.table_schema in ({0})", databaseIn);
+ var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
+ if (ds == null) return loc1;
+
+ var loc6 = new List();
+ var loc66 = new List();
+ foreach (var row in ds) {
+ var table_id = string.Concat(row[0]);
+ var schema = string.Concat(row[1]);
+ var table = string.Concat(row[2]);
+ var type = string.Concat(row[3]) == "VIEW" ? DbTableType.VIEW : DbTableType.TABLE;
+ if (database.Length == 1) {
+ table_id = table_id.Substring(table_id.IndexOf('.') + 1);
+ schema = "";
+ }
+ loc2.Add(table_id, new DbTableInfo { Id = table_id, Schema = schema, Name = table, Type = type });
+ loc3.Add(table_id, new Dictionary());
+ switch (type) {
+ case DbTableType.TABLE:
+ case DbTableType.VIEW:
+ loc6.Add(table.Replace("'", "''"));
+ break;
+ case DbTableType.StoreProcedure:
+ loc66.Add(table.Replace("'", "''"));
+ break;
+ }
+ }
+ if (loc6.Count == 0) return loc1;
+ var loc8 = "'" + string.Join("','", loc6.ToArray()) + "'";
+ var loc88 = "'" + string.Join("','", loc66.ToArray()) + "'";
+
+ sql = string.Format(@"
+select
+concat(a.table_schema, '.', a.table_name),
+a.column_name,
+a.data_type,
+ifnull(a.character_maximum_length, 0) 'len',
+a.column_type,
+case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
+case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
+a.column_comment 'comment'
+from information_schema.columns a
+where a.table_schema in ({1}) and a.table_name in ({0})
+", loc8, databaseIn);
+ ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
+ if (ds == null) return loc1;
+
+ foreach (var row in ds) {
+ string table_id = string.Concat(row[0]);
+ string column = string.Concat(row[1]);
+ string type = string.Concat(row[2]);
+ //long max_length = long.Parse(string.Concat(row[3]));
+ string sqlType = string.Concat(row[4]);
+ var m_len = Regex.Match(sqlType, @"\w+\((\d+)");
+ int max_length = m_len.Success ? int.Parse(m_len.Groups[1].Value) : -1;
+ bool is_nullable = string.Concat(row[5]) == "1";
+ bool is_identity = string.Concat(row[6]) == "1";
+ string comment = string.Concat(row[7]);
+ if (max_length == 0) max_length = -1;
+ if (database.Length == 1) {
+ table_id = table_id.Substring(table_id.IndexOf('.') + 1);
+ }
+ loc3[table_id].Add(column, new DbColumnInfo {
+ Name = column,
+ MaxLength = max_length,
+ IsIdentity = is_identity,
+ IsNullable = is_nullable,
+ IsPrimary = false,
+ DbTypeText = type,
+ DbTypeTextFull = sqlType,
+ Table = loc2[table_id],
+ Coment = comment
+ });
+ loc3[table_id][column].DbType = this.GetDbType(loc3[table_id][column]);
+ loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
+ }
+
+ sql = string.Format(@"
+select
+concat(a.constraint_schema, '.', a.table_name) 'table_id',
+a.column_name,
+concat(a.constraint_schema, '/', a.table_name, '/', a.constraint_name) 'index_id',
+1 'IsUnique',
+case when constraint_name = 'PRIMARY' then 1 else 0 end 'IsPrimaryKey',
+0 'IsClustered',
+0 'IsDesc'
+from information_schema.key_column_usage a
+where a.constraint_schema in ({1}) and a.table_name in ({0}) and isnull(position_in_unique_constraint)
+", loc8, databaseIn);
+ ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
+ if (ds == null) return loc1;
+
+ var indexColumns = new Dictionary>>();
+ var uniqueColumns = new Dictionary>>();
+ foreach (var row in ds) {
+ string table_id = string.Concat(row[0]);
+ string column = string.Concat(row[1]);
+ string index_id = string.Concat(row[2]);
+ bool is_unique = string.Concat(row[3]) == "1";
+ bool is_primary_key = string.Concat(row[4]) == "1";
+ bool is_clustered = string.Concat(row[5]) == "1";
+ int is_desc = int.Parse(string.Concat(row[6]));
+ if (database.Length == 1) {
+ table_id = table_id.Substring(table_id.IndexOf('.') + 1);
+ }
+ if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
+ var loc9 = loc3[table_id][column];
+ if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
+
+ Dictionary> loc10 = null;
+ List loc11 = null;
+ if (!indexColumns.TryGetValue(table_id, out loc10))
+ indexColumns.Add(table_id, loc10 = new Dictionary>());
+ if (!loc10.TryGetValue(index_id, out loc11))
+ loc10.Add(index_id, loc11 = new List());
+ loc11.Add(loc9);
+ if (is_unique) {
+ if (!uniqueColumns.TryGetValue(table_id, out loc10))
+ uniqueColumns.Add(table_id, loc10 = new Dictionary>());
+ if (!loc10.TryGetValue(index_id, out loc11))
+ loc10.Add(index_id, loc11 = new List());
+ loc11.Add(loc9);
+ }
+ }
+ foreach (string table_id in indexColumns.Keys) {
+ foreach (var columns in indexColumns[table_id].Values)
+ loc2[table_id].Indexes.Add(columns);
+ }
+ foreach (string table_id in uniqueColumns.Keys) {
+ foreach (var columns in uniqueColumns[table_id].Values) {
+ columns.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
+ loc2[table_id].Uniques.Add(columns);
+ }
+ }
+
+ sql = string.Format(@"
+select
+concat(a.constraint_schema, '.', a.table_name) 'table_id',
+a.column_name,
+concat(a.constraint_schema, '/', a.constraint_name) 'FKId',
+concat(a.referenced_table_schema, '.', a.referenced_table_name) 'ref_table_id',
+1 'IsForeignKey',
+a.referenced_column_name 'ref_column'
+from information_schema.key_column_usage a
+where a.constraint_schema in ({1}) and a.table_name in ({0}) and not isnull(position_in_unique_constraint)
+", loc8, databaseIn);
+ ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
+ if (ds == null) return loc1;
+
+ var fkColumns = new Dictionary>();
+ foreach (var row in ds) {
+ string table_id = string.Concat(row[0]);
+ string column = string.Concat(row[1]);
+ string fk_id = string.Concat(row[2]);
+ string ref_table_id = string.Concat(row[3]);
+ bool is_foreign_key = string.Concat(row[4]) == "1";
+ string referenced_column = string.Concat(row[5]);
+ if (database.Length == 1) {
+ table_id = table_id.Substring(table_id.IndexOf('.') + 1);
+ ref_table_id = ref_table_id.Substring(ref_table_id.IndexOf('.') + 1);
+ }
+ if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
+ var loc9 = loc3[table_id][column];
+ if (loc2.ContainsKey(ref_table_id) == false) continue;
+ var loc10 = loc2[ref_table_id];
+ var loc11 = loc3[ref_table_id][referenced_column];
+
+ 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 = loc10 });
+ loc13.Columns.Add(loc9);
+ loc13.ReferencedColumns.Add(loc11);
+ }
+ foreach (var table_id in fkColumns.Keys)
+ foreach (var fk in fkColumns[table_id].Values)
+ loc2[table_id].Foreigns.Add(fk);
+
+ 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.Uniques.Count > 0) {
+ foreach (var loc5 in loc4.Uniques[0]) {
+ loc5.IsPrimary = true;
+ loc4.Primarys.Add(loc5);
+ }
+ }
+ 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.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c1.Name) != null) != null;
+ bool b2 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c2.Name) != null) != null;
+ 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;
+ });
+ foreach(var loc4 in loc1) {
+ var dicUniques = new Dictionary>();
+ if (loc4.Primarys.Count > 0) dicUniques.Add(string.Join(",", loc4.Primarys.Select(a => a.Name)), loc4.Primarys);
+ foreach(var loc5 in loc4.Uniques) {
+ var dickey = string.Join(",", loc5.Select(a => a.Name));
+ if (dicUniques.ContainsKey(dickey)) continue;
+ dicUniques.Add(dickey, loc5);
+ }
+ loc4.Uniques = dicUniques.Values.ToList();
+ }
+
+ loc2.Clear();
+ loc3.Clear();
+ return loc1;
+ }
+
+ public List GetEnumsByDatabase(params string[] database) {
+ return new List();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FreeSql/Oracle/OracleExpression.cs b/FreeSql/Oracle/OracleExpression.cs
new file mode 100644
index 00000000..e8d5c180
--- /dev/null
+++ b/FreeSql/Oracle/OracleExpression.cs
@@ -0,0 +1,315 @@
+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.Oracle {
+ class OracleExpression : CommonExpression {
+
+ public OracleExpression(CommonUtils common) : base(common) { }
+
+ internal override string ExpressionLambdaToSqlOther(Expression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ switch (exp.NodeType) {
+ case ExpressionType.Call:
+ var callExp = exp as MethodCallExpression;
+ var objExp = callExp.Object;
+ var objType = objExp?.Type;
+ if (objType?.FullName == "System.Byte[]") return null;
+
+ var argIndex = 0;
+ if (objType == null && callExp.Method.DeclaringType.FullName == typeof(Enumerable).FullName) {
+ objExp = callExp.Arguments.FirstOrDefault();
+ objType = objExp?.Type;
+ argIndex++;
+ }
+ if (objType == null) objType = callExp.Method.DeclaringType;
+ if (objType != null) {
+ var left = objExp == null ? null : getExp(objExp);
+ if (objType.IsArray == true) {
+ switch (callExp.Method.Name) {
+ case "Contains":
+ //判断 in
+ return $"({getExp(callExp.Arguments[argIndex])}) in {left}";
+ }
+ }
+ }
+ break;
+ case ExpressionType.NewArrayInit:
+ var arrExp = exp as NewArrayExpression;
+ var arrSb = new StringBuilder();
+ arrSb.Append("(");
+ for (var a = 0; a < arrExp.Expressions.Count; a++) {
+ if (a > 0) arrSb.Append(",");
+ arrSb.Append(getExp(arrExp.Expressions[a]));
+ }
+ return arrSb.Append(")").ToString();
+ }
+ return null;
+ }
+
+ internal override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ if (exp.Expression == null) {
+ switch (exp.Member.Name) {
+ case "Empty": return "''";
+ }
+ return null;
+ }
+ var left = ExpressionLambdaToSql(exp.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ switch (exp.Member.Name) {
+ case "Length": return $"char_length({left})";
+ }
+ return null;
+ }
+ internal override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ if (exp.Expression == null) {
+ switch (exp.Member.Name) {
+ case "Now": return "now()";
+ case "UtcNow": return "utc_timestamp()";
+ case "Today": return "curdate()";
+ case "MinValue": return "cast('0001/1/1 0:00:00' as datetime)";
+ case "MaxValue": return "cast('9999/12/31 23:59:59' as datetime)";
+ }
+ return null;
+ }
+ var left = ExpressionLambdaToSql(exp.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ switch (exp.Member.Name) {
+ case "Date": return $"cast(date_format({left},'%Y-%m-%d') as datetime)";
+ case "TimeOfDay": return $"timestampdiff(microsecond, date_format({left},'%Y-%m-%d'), {left})";
+ case "DayOfWeek": return $"(dayofweek({left})-1)";
+ case "Day": return $"dayofmonth({left})";
+ case "DayOfYear": return $"dayofyear({left})";
+ case "Month": return $"month({left})";
+ case "Year": return $"year({left})";
+ case "Hour": return $"hour({left})";
+ case "Minute": return $"minute({left})";
+ case "Second": return $"second({left})";
+ case "Millisecond": return $"floor(microsecond({left})/1000)";
+ case "Ticks": return $"(timestampdiff(microsecond, '0001-1-1', {left})*10)";
+ }
+ return null;
+ }
+ internal override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ 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, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ switch (exp.Member.Name) {
+ case "Days": return $"(({left}) div {(long)1000000 * 60 * 60 * 24})";
+ case "Hours": return $"(({left}) div {(long)1000000 * 60 * 60} mod 24)";
+ case "Milliseconds": return $"(({left}) div 1000 mod 1000)";
+ case "Minutes": return $"(({left}) div {(long)1000000 * 60} mod 60)";
+ case "Seconds": return $"(({left}) div 1000000 mod 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;
+ }
+
+ internal override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ if (exp.Object == null) {
+ switch (exp.Method.Name) {
+ case "IsNullOrEmpty":
+ var arg1 = getExp(exp.Arguments[0]);
+ return $"({arg1} is null or {arg1} = '')";
+ }
+ } 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 (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
+ if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
+ if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
+ return $"({left}) LIKE concat('%', {args0Value}, '%')";
+ 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":
+ var indexOfFindStr = getExp(exp.Arguments[0]);
+ if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") {
+ var locateArgs1 = getExp(exp.Arguments[1]);
+ if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
+ else locateArgs1 += "+1";
+ return $"(locate({left}, {indexOfFindStr}, {locateArgs1})-1)";
+ }
+ return $"(locate({left}, {indexOfFindStr})-1)";
+ case "PadLeft":
+ if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
+ return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
+ case "PadRight":
+ if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
+ return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
+ case "Trim":
+ case "TrimStart":
+ case "TrimEnd":
+ if (exp.Arguments.Count == 0) {
+ if (exp.Method.Name == "Trim") return $"trim({left})";
+ if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
+ if (exp.Method.Name == "TrimEnd") return $"rtrim({left})";
+ }
+ 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) {
+ if (exp.Method.Name == "Trim") left = $"trim({getExp(argsTrim01)} from {left})";
+ if (exp.Method.Name == "TrimStart") left = $"trim(leading {getExp(argsTrim01)} from {left})";
+ if (exp.Method.Name == "TrimEnd") left = $"trim(trailing {getExp(argsTrim01)} from {left})";
+ }
+ }
+ return left;
+ case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
+ case "CompareTo": return $"strcmp({left}, {getExp(exp.Arguments[0])})";
+ case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
+ }
+ }
+ throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
+ }
+ internal override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ 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 $"truncate({getExp(exp.Arguments[0])}, 0)";
+ }
+ throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
+ }
+ internal override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ if (exp.Object == null) {
+ switch (exp.Method.Name) {
+ case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))";
+ case "DaysInMonth": return $"dayofmonth(last_day(concat({getExp(exp.Arguments[0])}, '-', {getExp(exp.Arguments[1])}, '-01')))";
+ case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
+
+ case "IsLeapYear":
+ var isLeapYearArgs1 = getExp(exp.Arguments[0]);
+ return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
+
+ case "Parse": return $"cast({getExp(exp.Arguments[0])} as datetime)";
+ case "ParseExact":
+ case "TryParse":
+ case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as datetime)";
+ }
+ } else {
+ var left = getExp(exp.Object);
+ var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
+ switch (exp.Method.Name) {
+ case "Add": return $"date_add({left}, interval ({args1}) microsecond)";
+ case "AddDays": return $"date_add({left}, interval ({args1}) day)";
+ case "AddHours": return $"date_add({left}, interval ({args1}) hour)";
+ case "AddMilliseconds": return $"date_add({left}, interval ({args1})*1000 microsecond)";
+ case "AddMinutes": return $"date_add({left}, interval ({args1}) minute)";
+ case "AddMonths": return $"date_add({left}, interval ({args1}) month)";
+ case "AddSeconds": return $"date_add({left}, interval ({args1}) second)";
+ case "AddTicks": return $"date_add({left}, interval ({args1})/10 microsecond)";
+ case "AddYears": return $"date_add({left}, interval ({args1}) year)";
+ case "Subtract":
+ if (exp.Arguments[0].Type.FullName == "System.DateTime" || exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault()?.FullName == "System.DateTime")
+ return $"timestampdiff(microsecond, {args1}, {left})";
+ if (exp.Arguments[0].Type.FullName == "System.TimeSpan" || exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault()?.FullName == "System.TimeSpan")
+ return $"date_sub({left}, interval ({args1}) microsecond)";
+ break;
+ case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
+ case "CompareTo": return $"(({left}) - ({getExp(exp.Arguments[0])}))";
+ case "ToString": return $"date_format({left}, '%Y-%m-%d %H:%i:%s.%f')";
+ }
+ }
+ throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
+ }
+ internal override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ 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 $"cast({getExp(exp.Arguments[0])} as signed)";
+ case "ParseExact":
+ case "TryParse":
+ case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as signed)";
+ }
+ } 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} = {getExp(exp.Arguments[0])})";
+ case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
+ case "ToString": return $"cast({left} as char)";
+ }
+ }
+ throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
+ }
+ internal override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) {
+ Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName);
+ if (exp.Object == null) {
+ switch (exp.Method.Name) {
+ case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
+ case "ToByte": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
+ case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as char), 1, 1)";
+ case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as datetime)";
+ case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
+ case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))";
+ case "ToInt16":
+ case "ToInt32":
+ case "ToInt64":
+ case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as signed)";
+ case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))";
+ case "ToString": return $"cast({getExp(exp.Arguments[0])} as char)";
+ case "ToUInt16":
+ case "ToUInt32":
+ case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
+ }
+ }
+ throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
+ }
+ }
+}
diff --git a/FreeSql/Oracle/OracleProvider.cs b/FreeSql/Oracle/OracleProvider.cs
new file mode 100644
index 00000000..7e3c8bd8
--- /dev/null
+++ b/FreeSql/Oracle/OracleProvider.cs
@@ -0,0 +1,49 @@
+using FreeSql.Internal;
+using FreeSql.Internal.CommonProvider;
+using FreeSql.Oracle.Curd;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+
+namespace FreeSql.Oracle {
+
+ class OracleProvider : IFreeSql {
+
+ public ISelect Select() where T1 : class => new OracleSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
+ public ISelect Select(object dywhere) where T1 : class => new OracleSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
+ public IInsert Insert() where T1 : class => new OracleInsert(this, this.InternalCommonUtils, this.InternalCommonExpression);
+ public IInsert Insert(T1 source) where T1 : class => this.Insert().AppendData(source);
+ public IInsert Insert(IEnumerable source) where T1 : class => this.Insert().AppendData(source);
+ public IUpdate Update() where T1 : class => new OracleUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
+ public IUpdate Update(object dywhere) where T1 : class => new OracleUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
+ public IDelete Delete() where T1 : class => new OracleDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
+ public IDelete Delete(object dywhere) where T1 : class => new OracleDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
+
+ public IAdo Ado { get; }
+ public ICache Cache { get; }
+ public ICodeFirst CodeFirst { get; }
+ public IDbFirst DbFirst { get; }
+ public OracleProvider(IDistributedCache cache, ILogger log, string masterConnectionString, string[] slaveConnectionString) {
+ if (log == null) log = new LoggerFactory(new[] { new Microsoft.Extensions.Logging.Debug.DebugLoggerProvider() }).CreateLogger("FreeSql.Oracle");
+
+ this.InternalCommonUtils = new OracleUtils(this);
+ this.InternalCommonExpression = new OracleExpression(this.InternalCommonUtils);
+
+ this.Cache = new CacheProvider(cache, log);
+ this.Ado = new OracleAdo(this.InternalCommonUtils, this.Cache, log, masterConnectionString, slaveConnectionString);
+
+ this.DbFirst = new OracleDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
+ this.InternalCommonUtils.CodeFirst = this.CodeFirst = new OracleCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
+ }
+
+ internal CommonUtils InternalCommonUtils { get; }
+ internal CommonExpression InternalCommonExpression { get; }
+
+ public void Transaction(Action handler) => Ado.Transaction(handler);
+
+ public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
+ }
+}
diff --git a/FreeSql/Oracle/OracleUtils.cs b/FreeSql/Oracle/OracleUtils.cs
new file mode 100644
index 00000000..6e6a095c
--- /dev/null
+++ b/FreeSql/Oracle/OracleUtils.cs
@@ -0,0 +1,47 @@
+using FreeSql.Internal;
+using FreeSql.Internal.Model;
+using Oracle.ManagedDataAccess.Client;
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+
+namespace FreeSql.Oracle {
+
+ class OracleUtils : CommonUtils {
+ IFreeSql _orm;
+ public OracleUtils(IFreeSql orm) {
+ _orm = orm;
+ }
+
+ internal override DbParameter AppendParamter(List _params, string parameterName, Type type, object value) {
+ if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
+ else if (_orm.CodeFirst.IsSyncStructureToLower) parameterName = parameterName.ToLower();
+ var ret = new OracleParameter { ParameterName = $"@{parameterName}", Value = value };
+ var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
+ if (tp != null) {
+ ret.OracleDbType = (OracleDbType)tp.Value;
+ }
+ _params?.Add(ret);
+ return ret;
+ }
+
+ internal override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
+ Utils.GetDbParamtersByObject(sql, obj, "@", (name, type, value) => {
+ var ret = new OracleParameter { ParameterName = $"@{name}", Value = value };
+ var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
+ if (tp != null) {
+ ret.OracleDbType = (OracleDbType)tp.Value;
+ }
+ return ret;
+ });
+
+ internal override string FormatSql(string sql, params object[] args) => sql?.FormatOracleSQL(args);
+ internal override string QuoteSqlName(string name) => $"\"{name.Trim('"').Replace(".", "\".\"")}\"";
+ internal override string QuoteParamterName(string name) => $"@{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
+ internal override string IsNull(string sql, object value) => $"nvl({sql}, {value})";
+ internal override string StringConcat(string left, string right, Type leftType, Type rightType) => $"{left} || {right}";
+
+ internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
+ internal override string QuoteReadColumn(Type type, string columnName) => columnName;
+ }
+}
diff --git a/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs b/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs
index 37089eee..4ce0d307 100644
--- a/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs
+++ b/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs
@@ -16,13 +16,23 @@ namespace FreeSql.PostgreSQL.Curd {
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
- return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(_table.Columns.Where(a => a.Value.Attribute.IsIdentity).FirstOrDefault().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
+ var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity);
+ if (identCols.Any() == false) {
+ _orm.Ado.ExecuteNonQuery(CommandType.Text, sql, _params);
+ return 0;
+ }
+ return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
}
async public override Task ExecuteIdentityAsync() {
var sql = this.ToSql();
if (string.IsNullOrEmpty(sql)) return 0;
- return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(_table.Columns.Where(a => a.Value.Attribute.IsIdentity).FirstOrDefault().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
+ var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity);
+ if (identCols.Any() == false) {
+ await _orm.Ado.ExecuteNonQueryAsync(CommandType.Text, sql, _params);
+ return 0;
+ }
+ return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
}
public override List ExecuteInserted() {
diff --git a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs
index 96b0dfe8..411147a3 100644
--- a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs
+++ b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs
@@ -25,6 +25,7 @@ namespace FreeSql.PostgreSQL {
}
}
}
+
static DateTime dt1970 = new DateTime(1970, 1, 1);
public override object AddslashesProcessParam(object param) {
bool isdic = false;
diff --git a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs
index 7aca8408..badeaa43 100644
--- a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs
+++ b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs
@@ -136,7 +136,7 @@ namespace FreeSql.PostgreSQL {
}
}
- public static class DbConnectionExtensions {
+ static class DbConnectionExtensions {
public static bool Ping(this DbConnection that) {
try {
diff --git a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesExtensions.cs b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesExtensions.cs
index 647a27ed..72f942fe 100644
--- a/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesExtensions.cs
+++ b/FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesExtensions.cs
@@ -1,6 +1,7 @@
using Npgsql.LegacyPostgis;
using NpgsqlTypes;
using System;
+using System.Collections;
public static partial class PostgreSQLTypesExtensions {
///
@@ -30,4 +31,35 @@ public static partial class PostgreSQLTypesExtensions {
double radLng2 = (double)(point.X) * Math.PI / 180d;
return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
}
+
+ public static 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;
+ }
+
+ public static NpgsqlRange ToNpgsqlRange(this string that) {
+ var s = that;
+ if (string.IsNullOrEmpty(s) || s == "empty") return NpgsqlRange.Empty;
+ string s1 = s.Trim('(', ')', '[', ']');
+ string[] ss = s1.Split(new char[] { ',' }, 2);
+ if (ss.Length != 2) return NpgsqlRange.Empty;
+ T t1 = default(T);
+ T t2 = default(T);
+ if (!string.IsNullOrEmpty(ss[0])) t1 = (T)Convert.ChangeType(ss[0], typeof(T));
+ if (!string.IsNullOrEmpty(ss[1])) t2 = (T)Convert.ChangeType(ss[1], typeof(T));
+ return new NpgsqlRange(t1, s[0] == '[', s[0] == '(', t2, s[s.Length - 1] == ']', s[s.Length - 1] == ')');
+ }
}
\ No newline at end of file
diff --git a/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs b/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs
index 92bad45d..96a68414 100644
--- a/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs
+++ b/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs
@@ -132,14 +132,14 @@ namespace FreeSql.PostgreSQL {
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
if (tboldname?.Length == 1) tboldname = new[] { "public", tboldname[0] };
- if (string.Compare(tbname[0], "public", true) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, $"select 1 from pg_namespace where nspname='{tbname[0]}'") == null) //创建模式
+ if (string.Compare(tbname[0], "public", true) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, " select 1 from pg_namespace where nspname={0}".FormatPostgreSQL(tbname[0])) == null) //创建模式
sb.Append("CREATE SCHEMA IF NOT EXISTS ").Append(tbname[0]).Append(";\r\n");
var sbalter = new StringBuilder();
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
- if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format("select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tbname)) == null) { //表不存在
+ if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tbname)) == null) { //表不存在
if (tboldname != null) {
- if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format("select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tboldname)) == null)
+ if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tboldname)) == null)
//旧表不存在
tboldname = null;
}
@@ -171,7 +171,8 @@ namespace FreeSql.PostgreSQL {
tboldname = null; //如果新表已经存在,不走改表名逻辑
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
- var sql = @"select
+ var sql = @"
+select
a.attname,
t.typname,
case when a.atttypmod > 0 and a.atttypmod < 32767 then a.atttypmod - 4 else a.attlen end len,
@@ -194,7 +195,7 @@ where ns.nspname = {0} and c.relname = {1}".FormatPostgreSQL(tboldname ?? tbname
var type = string.Concat(a[1]);
var sqlType = string.Concat(a[3]);
var max_length = long.Parse(string.Concat(a[2]));
- switch (sqlType) {
+ 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;
}
@@ -288,7 +289,7 @@ where ns.nspname = {0} and c.relname = {1}".FormatPostgreSQL(tboldname ?? tbname
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");
+ sb.Append(" SELECT case when max(").Append(colname2).Append(") is null then 0 else setval('").Append(seqname).Append("', max(").Append(colname2).Append(")) end FROM ").Append(tbname2).Append(";\r\n");
}
}
return sb.Length == 0 ? null : sb.ToString();
diff --git a/FreeSql/PostgreSQL/PostgreSQLDbFirst.cs b/FreeSql/PostgreSQL/PostgreSQLDbFirst.cs
index d48cedb5..dfaf9e4b 100644
--- a/FreeSql/PostgreSQL/PostgreSQLDbFirst.cs
+++ b/FreeSql/PostgreSQL/PostgreSQLDbFirst.cs
@@ -1,10 +1,17 @@
using FreeSql.DatabaseModel;
using FreeSql.Internal;
-using MySql.Data.MySqlClient;
+using Newtonsoft.Json.Linq;
+using Npgsql.LegacyPostgis;
+using NpgsqlTypes;
+using SafeObjectPool;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Data;
+using System.Data.Common;
using System.Linq;
+using System.Net;
+using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
namespace FreeSql.PostgreSQL {
@@ -18,107 +25,168 @@ namespace FreeSql.PostgreSQL {
_commonExpression = commonExpression;
}
- public int GetDbType(DbColumnInfo column) => (int)GetMySqlDbType(column);
- MySqlDbType GetMySqlDbType(DbColumnInfo column) {
- var is_unsigned = column.DbTypeTextFull.ToLower().EndsWith(" unsigned");
- switch (column.DbTypeText.ToLower()) {
- case "bit": return MySqlDbType.Bit;
+ public int GetDbType(DbColumnInfo column) => (int)GetNpgsqlDbType(column);
+ NpgsqlDbType GetNpgsqlDbType(DbColumnInfo column) {
+ var dbtype = column.DbTypeText;
+ var isarray = dbtype.EndsWith("[]");
+ if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2);
+ NpgsqlDbType ret = NpgsqlDbType.Unknown;
+ switch (dbtype.ToLower().TrimStart('_')) {
+ case "int2": ret = NpgsqlDbType.Smallint;break;
+ case "int4": ret = NpgsqlDbType.Integer; break;
+ case "int8": ret = NpgsqlDbType.Bigint; break;
+ case "numeric": ret = NpgsqlDbType.Numeric; break;
+ case "float4": ret = NpgsqlDbType.Real; break;
+ case "float8": ret = NpgsqlDbType.Double; break;
+ case "money": ret = NpgsqlDbType.Money; break;
- case "tinyint": return is_unsigned ? MySqlDbType.UByte : MySqlDbType.Byte;
- case "smallint": return is_unsigned ? MySqlDbType.UInt16 : MySqlDbType.Int16;
- case "mediumint": return is_unsigned ? MySqlDbType.UInt24 : MySqlDbType.Int24;
- case "int": return is_unsigned ? MySqlDbType.UInt32 : MySqlDbType.Int32;
- case "bigint": return is_unsigned ? MySqlDbType.UInt64 : MySqlDbType.Int64;
+ case "bpchar": ret = NpgsqlDbType.Char; break;
+ case "varchar": ret = NpgsqlDbType.Varchar; break;
+ case "text": ret = NpgsqlDbType.Text; break;
- case "real":
- case "double": return MySqlDbType.Double;
- case "float": return MySqlDbType.Float;
- case "numeric":
- case "decimal": return MySqlDbType.Decimal;
+ case "timestamp": ret = NpgsqlDbType.Timestamp; break;
+ case "timestamptz": ret = NpgsqlDbType.TimestampTz; break;
+ case "date": ret = NpgsqlDbType.Date; break;
+ case "time": ret = NpgsqlDbType.Time; break;
+ case "timetz": ret = NpgsqlDbType.TimeTz; break;
+ case "interval": ret = NpgsqlDbType.Interval; break;
- case "year": return MySqlDbType.Year;
- case "time": return MySqlDbType.Time;
- case "date": return MySqlDbType.Date;
- case "timestamp": return MySqlDbType.Timestamp;
- case "datetime": return MySqlDbType.DateTime;
+ case "bool": ret = NpgsqlDbType.Boolean; break;
+ case "bytea": ret = NpgsqlDbType.Bytea; break;
+ case "bit": ret = NpgsqlDbType.Bit; break;
+ case "varbit": ret = NpgsqlDbType.Varbit; break;
- case "tinyblob": return MySqlDbType.TinyBlob;
- case "blob": return MySqlDbType.Blob;
- case "mediumblob": return MySqlDbType.MediumBlob;
- case "longblob": return MySqlDbType.LongBlob;
+ case "point": ret = NpgsqlDbType.Point; break;
+ case "line": ret = NpgsqlDbType.Line; break;
+ case "lseg": ret = NpgsqlDbType.LSeg; break;
+ case "box": ret = NpgsqlDbType.Box; break;
+ case "path": ret = NpgsqlDbType.Path; break;
+ case "polygon": ret = NpgsqlDbType.Polygon; break;
+ case "circle": ret = NpgsqlDbType.Circle; break;
- case "binary": return MySqlDbType.Binary;
- case "varbinary": return MySqlDbType.VarBinary;
+ case "cidr": ret = NpgsqlDbType.Cidr; break;
+ case "inet": ret = NpgsqlDbType.Inet; break;
+ case "macaddr": ret = NpgsqlDbType.MacAddr; break;
- case "tinytext": return MySqlDbType.TinyText;
- case "text": return MySqlDbType.Text;
- case "mediumtext": return MySqlDbType.MediumText;
- case "longtext": return MySqlDbType.LongText;
+ case "json": ret = NpgsqlDbType.Json; break;
+ case "jsonb": ret = NpgsqlDbType.Jsonb; break;
+ case "uuid": ret = NpgsqlDbType.Uuid; break;
- case "char": return MySqlDbType.String;
- case "varchar": return MySqlDbType.VarChar;
+ case "int4range": ret = NpgsqlDbType.Range | NpgsqlDbType.Integer; break;
+ case "int8range": ret = NpgsqlDbType.Range | NpgsqlDbType.Bigint; break;
+ case "numrange": ret = NpgsqlDbType.Range | NpgsqlDbType.Numeric; break;
+ case "tsrange": ret = NpgsqlDbType.Range | NpgsqlDbType.Timestamp; break;
+ case "tstzrange": ret = NpgsqlDbType.Range | NpgsqlDbType.TimestampTz; break;
+ case "daterange": ret = NpgsqlDbType.Range | NpgsqlDbType.Date; break;
- case "set": return MySqlDbType.Set;
- case "enum": return MySqlDbType.Enum;
-
- case "point": return MySqlDbType.Geometry;
- case "linestring": return MySqlDbType.Geometry;
- case "polygon": return MySqlDbType.Geometry;
- case "geometry": return MySqlDbType.Geometry;
- case "multipoint": return MySqlDbType.Geometry;
- case "multilinestring": return MySqlDbType.Geometry;
- case "multipolygon": return MySqlDbType.Geometry;
- case "geometrycollection": return MySqlDbType.Geometry;
- default: return MySqlDbType.String;
+ case "hstore": ret = NpgsqlDbType.Hstore; break;
+ case "geometry": ret = NpgsqlDbType.Geometry; break;
}
+ return isarray ? (ret | NpgsqlDbType.Array) : ret;
}
static readonly Dictionary _dicDbToCs = new Dictionary() {
- { (int)MySqlDbType.Bit, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
+ { (int)NpgsqlDbType.Smallint, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
+ { (int)NpgsqlDbType.Integer, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
+ { (int)NpgsqlDbType.Bigint, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
+ { (int)NpgsqlDbType.Numeric, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
+ { (int)NpgsqlDbType.Real, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
+ { (int)NpgsqlDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
+ { (int)NpgsqlDbType.Money, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
- { (int)MySqlDbType.Byte, ("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetByte") },
- { (int)MySqlDbType.Int16, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
- { (int)MySqlDbType.Int24, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
- { (int)MySqlDbType.Int32, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
- { (int)MySqlDbType.Int64, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
+ { (int)NpgsqlDbType.Char, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)NpgsqlDbType.Varchar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)NpgsqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
- { (int)MySqlDbType.UByte, ("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
- { (int)MySqlDbType.UInt16, ("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt16") },
- { (int)MySqlDbType.UInt24, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
- { (int)MySqlDbType.UInt32, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
- { (int)MySqlDbType.UInt64, ("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
+ { (int)NpgsqlDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+ { (int)NpgsqlDbType.TimestampTz, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+ { (int)NpgsqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+ { (int)NpgsqlDbType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
+ { (int)NpgsqlDbType.TimeTz, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
+ { (int)NpgsqlDbType.Interval, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
- { (int)MySqlDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
- { (int)MySqlDbType.Float, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
- { (int)MySqlDbType.Decimal, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
+ { (int)NpgsqlDbType.Boolean, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
+ { (int)NpgsqlDbType.Bytea, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Bit, ("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray), typeof(BitArray), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Varbit, ("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray), typeof(BitArray), "{0}", "GetValue") },
- { (int)MySqlDbType.Year, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
- { (int)MySqlDbType.Time, ("(TimeSpan?)", "TimeSpan.FromSeconds(double.Parse({0}))", "{0}.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
- { (int)MySqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
- { (int)MySqlDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.TotalSeconds.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
- { (int)MySqlDbType.DateTime, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
+ { (int)NpgsqlDbType.Point, ("(NpgsqlPoint?)", "NpgsqlPoint.Parse({0})", "{0}.ToString()", "NpgsqlPoint", typeof(NpgsqlPoint), typeof(NpgsqlPoint?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Line, ("(NpgsqlLine?)", "NpgsqlLine.Parse({0})", "{0}.ToString()", "NpgsqlLine", typeof(NpgsqlLine), typeof(NpgsqlLine?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.LSeg, ("(NpgsqlLSeg?)", "NpgsqlLSeg.Parse({0})", "{0}.ToString()", "NpgsqlLSeg", typeof(NpgsqlLSeg), typeof(NpgsqlLSeg?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Box, ("(NpgsqlBox?)", "NpgsqlBox.Parse({0})", "{0}.ToString()", "NpgsqlBox", typeof(NpgsqlBox), typeof(NpgsqlBox?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Path, ("(NpgsqlPath?)", "NpgsqlPath.Parse({0})", "{0}.ToString()", "NpgsqlPath", typeof(NpgsqlPath), typeof(NpgsqlPath?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Polygon, ("(NpgsqlPolygon?)", "NpgsqlPolygon.Parse({0})", "{0}.ToString()", "NpgsqlPolygon", typeof(NpgsqlPolygon), typeof(NpgsqlPolygon?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Circle, ("(NpgsqlCircle?)", "NpgsqlCircle.Parse({0})", "{0}.ToString()", "NpgsqlCircle", typeof(NpgsqlCircle), typeof(NpgsqlCircle?), "{0}", "GetValue") },
- { (int)MySqlDbType.TinyBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
- { (int)MySqlDbType.Blob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
- { (int)MySqlDbType.MediumBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
- { (int)MySqlDbType.LongBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Cidr, ("((IPAddress, int)?)", "(IPAddress, int)({0})", "{0}.ToString()", "(IPAddress, int)", typeof((IPAddress, int)), typeof((IPAddress, int)?), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Inet, ("(IPAddress)", "IPAddress.Parse({0})", "{0}.ToString()", "IPAddress", typeof(IPAddress), typeof(IPAddress), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.MacAddr, ("(PhysicalAddress?)", "PhysicalAddress.Parse({0})", "{0}.ToString()", "PhysicalAddress", typeof(PhysicalAddress), typeof(PhysicalAddress), "{0}", "GetValue") },
- { (int)MySqlDbType.Binary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
- { (int)MySqlDbType.VarBinary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
+ { (int)NpgsqlDbType.Json, ("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken), "{0}", "GetString") },
+ { (int)NpgsqlDbType.Jsonb, ("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken), "{0}", "GetString") },
+ { (int)NpgsqlDbType.Uuid, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
- { (int)MySqlDbType.TinyText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
- { (int)MySqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
- { (int)MySqlDbType.MediumText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
- { (int)MySqlDbType.LongText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
+ { (int)(NpgsqlDbType.Range | NpgsqlDbType.Integer), ("(NpgsqlRange?)", "{0}.ToNpgsqlRange()", "{0}.ToString()", "NpgsqlRange", typeof(NpgsqlRange), typeof(NpgsqlRange?), "{0}", "GetString") },
+ { (int)(NpgsqlDbType.Range | NpgsqlDbType.Bigint), ("(NpgsqlRange?)", "{0}.ToNpgsqlRange()", "{0}.ToString()", "NpgsqlRange", typeof(NpgsqlRange), typeof(NpgsqlRange?), "{0}", "GetString") },
+ { (int)(NpgsqlDbType.Range | NpgsqlDbType.Numeric), ("(NpgsqlRange?)", "{0}.ToNpgsqlRange