From 78c1ce6f92a81d7df4c1aa988e3610abae753450 Mon Sep 17 00:00:00 2001 From: viogroza <86362427+viogroza@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:16:03 +0300 Subject: [PATCH] Database: Oracle Ref Cursor support (24.11) (#435) Allow the type of the parameter to be changed Change the type of the parameter to OracleDbType.RefCursor Change the type of the parameter for a few known types --- .../ExecuteNonQuery.cs | 14 ++-- .../ExecuteQuery.cs | 17 +++-- .../DatabaseConnectionTests.cs | 5 +- .../UiPath.Database/DatabaseConnection.cs | 74 ++++++++++++++++--- .../Database/UiPath.Database/ParameterInfo.cs | 17 +++++ 5 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 Activities/Database/UiPath.Database/ParameterInfo.cs diff --git a/Activities/Database/UiPath.Database.Activities/ExecuteNonQuery.cs b/Activities/Database/UiPath.Database.Activities/ExecuteNonQuery.cs index cfd1e9d1..76624947 100644 --- a/Activities/Database/UiPath.Database.Activities/ExecuteNonQuery.cs +++ b/Activities/Database/UiPath.Database.Activities/ExecuteNonQuery.cs @@ -49,7 +49,7 @@ protected async override Task> ExecuteInternalA { throw new ArgumentException(Resources.TimeoutMSException, "TimeoutMS"); } - Dictionary> parameters = null; + Dictionary parameters = null; var continueOnError = ContinueOnError.Get(context); try { @@ -61,10 +61,10 @@ protected async override Task> ExecuteInternalA if (Parameters != null) { - parameters = new Dictionary>(); + parameters = new Dictionary(); foreach (var param in Parameters) { - parameters.Add(param.Key, new Tuple(param.Value.Get(context), param.Value.Direction)); + parameters.Add(param.Key, new ParameterInfo() { Value = param.Value.Get(context), Direction = param.Value.Direction, Type = param.Value.ArgumentType }); } } ConnectionHelper.ConnectionValidation(existingConnection, connSecureString, connString, provName); @@ -105,7 +105,7 @@ protected async override Task> ExecuteInternalA var currentParam = Parameters[param.Key]; if (currentParam.Direction == ArgumentDirection.Out || currentParam.Direction == ArgumentDirection.InOut) { - currentParam.Set(asyncCodeActivityContext, param.Value.Item1); + currentParam.Set(asyncCodeActivityContext, param.Value.Value); } } }; @@ -115,15 +115,15 @@ protected async override Task> ExecuteInternalA private class DBExecuteCommandResult { public int Result { get; } - public Dictionary> ParametersBind { get; } + public Dictionary ParametersBind { get; } public DBExecuteCommandResult() { this.Result = 0; - this.ParametersBind = new Dictionary>(); + this.ParametersBind = new Dictionary(); } - public DBExecuteCommandResult(int result, Dictionary> parametersBind) + public DBExecuteCommandResult(int result, Dictionary parametersBind) { this.Result = result; this.ParametersBind = parametersBind; diff --git a/Activities/Database/UiPath.Database.Activities/ExecuteQuery.cs b/Activities/Database/UiPath.Database.Activities/ExecuteQuery.cs index b0c446bb..71788efe 100644 --- a/Activities/Database/UiPath.Database.Activities/ExecuteQuery.cs +++ b/Activities/Database/UiPath.Database.Activities/ExecuteQuery.cs @@ -50,7 +50,7 @@ protected async override Task> ExecuteInternalA { throw new ArgumentException(Resources.TimeoutMSException, "TimeoutMS"); } - Dictionary> parameters = null; + Dictionary parameters = null; var continueOnError = ContinueOnError.Get(context); try { @@ -62,10 +62,13 @@ protected async override Task> ExecuteInternalA ConnectionHelper.ConnectionValidation(existingConnection, connSecureString, connString, provName); if (Parameters != null) { - parameters = new Dictionary>(); + parameters = new Dictionary(); foreach (var param in Parameters) { - parameters.Add(param.Key, new Tuple(param.Value.Get(context), param.Value.Direction)); + parameters.Add(param.Key, new ParameterInfo() { + Value = param.Value.Get(context), + Direction = param.Value.Direction, + Type = param.Value.ArgumentType}); } } @@ -107,7 +110,7 @@ protected async override Task> ExecuteInternalA var currentParam = Parameters[param.Key]; if (currentParam.Direction == ArgumentDirection.Out || currentParam.Direction == ArgumentDirection.InOut) { - currentParam.Set(asyncCodeActivityContext, param.Value.Item1); + currentParam.Set(asyncCodeActivityContext, param.Value.Value); } } }; @@ -116,15 +119,15 @@ protected async override Task> ExecuteInternalA private class DBExecuteQueryResult { public DataTable Result { get; } - public Dictionary> ParametersBind { get; } + public Dictionary ParametersBind { get; } public DBExecuteQueryResult() { this.Result = new DataTable(); - this.ParametersBind = new Dictionary>(); + this.ParametersBind = new Dictionary(); } - public DBExecuteQueryResult(DataTable result, Dictionary> parametersBind) + public DBExecuteQueryResult(DataTable result, Dictionary parametersBind) { this.Result = result; this.ParametersBind = parametersBind; diff --git a/Activities/Database/UiPath.Database.Tests/DatabaseConnectionTests.cs b/Activities/Database/UiPath.Database.Tests/DatabaseConnectionTests.cs index bf2db64f..51c63f26 100644 --- a/Activities/Database/UiPath.Database.Tests/DatabaseConnectionTests.cs +++ b/Activities/Database/UiPath.Database.Tests/DatabaseConnectionTests.cs @@ -89,7 +89,10 @@ public void TestSize(string provider) param.SetReturnsDefault(ParameterDirection.InputOutput); var databaseConnection = new DatabaseConnection().Initialize(con.Object); - var parameters = new Dictionary>() { { "param1", new Tuple("", ArgumentDirection.Out) } }; + var parameters = new Dictionary() { + { "param1", new ParameterInfo() {Value = "", Direction = ArgumentDirection.Out} + } + }; databaseConnection.ExecuteQuery("TestProcedure", parameters, 0); if (provider.ToLower().Contains("oracle")) Assert.True(param.Object.Size == 1000000); diff --git a/Activities/Database/UiPath.Database/DatabaseConnection.cs b/Activities/Database/UiPath.Database/DatabaseConnection.cs index 31545a27..0dcea240 100644 --- a/Activities/Database/UiPath.Database/DatabaseConnection.cs +++ b/Activities/Database/UiPath.Database/DatabaseConnection.cs @@ -15,6 +15,7 @@ using UiPath.Database.Properties; using UiPath.Data.ConnectionUI.Dialog.Workaround; using UiPath.Robot.Activities.Api; +using Oracle.ManagedDataAccess.Types; namespace UiPath.Database { @@ -57,7 +58,7 @@ public DatabaseConnection Initialize(string connectionString, string providerNam DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", Microsoft.Data.SqlClient.SqlClientFactory.Instance); //OLEDB driver is Windows propietary - there is no support for other OS - if(_isWindows) + if (_isWindows) DbProviderFactories.RegisterFactory("System.Data.OleDb", System.Data.OleDb.OleDbFactory.Instance); DbProviderFactories.RegisterFactory("System.Data.Odbc", System.Data.Odbc.OdbcFactory.Instance); @@ -85,7 +86,7 @@ public virtual void BeginTransaction() _transaction = _connection.BeginTransaction(); } - public virtual DataTable ExecuteQuery(string sql, Dictionary> parameters, int commandTimeout, CommandType commandType = CommandType.Text) + public virtual DataTable ExecuteQuery(string sql, Dictionary parameters, int commandTimeout, CommandType commandType = CommandType.Text) { OpenConnection(); SetupCommand(sql, parameters, commandTimeout, commandType); @@ -95,12 +96,13 @@ public virtual DataTable ExecuteQuery(string sql, Dictionary(dbParam.Value, WokflowParameterDirectionToDbParameter(dbParam.Direction)); + parameters[dbParam.ParameterName] = new ParameterInfo() { Value = dbParam.Value, + Direction = WokflowParameterDirectionToDbParameter(dbParam.Direction) }; } return dt; } - public virtual int Execute(string sql, Dictionary> parameters, int commandTimeout, CommandType commandType = CommandType.Text) + public virtual int Execute(string sql, Dictionary parameters, int commandTimeout, CommandType commandType = CommandType.Text) { OpenConnection(); SetupCommand(sql, parameters, commandTimeout, commandType); @@ -109,7 +111,11 @@ public virtual int Execute(string sql, Dictionary(dbParam.Value, WokflowParameterDirectionToDbParameter(dbParam.Direction)); + parameters[dbParam.ParameterName] = new ParameterInfo() + { + Value = dbParam.Value, + Direction = WokflowParameterDirectionToDbParameter(dbParam.Direction) + }; } return result; } @@ -124,7 +130,7 @@ public virtual int InsertDataTable(string tableName, DataTable dataTable) { return InsertDataTableInternal(tableName, dataTable, true); } - catch(Exception e) + catch (Exception e) { firstException = e; } @@ -442,7 +448,7 @@ private void OpenConnection() } } - private void SetupCommand(string sql, Dictionary> parameters, int commandTimeout, CommandType commandType = CommandType.Text) + private void SetupCommand(string sql, Dictionary parameters, int commandTimeout, CommandType commandType = CommandType.Text) { if (_connection == null) { @@ -461,6 +467,7 @@ private void SetupCommand(string sql, Dictionary + /// The mapping of C# Type to OracleDbType or to DbType is not 1-1 + /// There are a lot of conversions behind the scene that are done + /// Most of the conversions work ok with string type + /// Here is some reference (it might not be up to date) + /// https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings + /// https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/oracle-data-type-mappings + /// For now leave only a few conversions + /// + private readonly Dictionary _oracleMappings = new Dictionary() + { + { typeof(OracleRefCursor), OracleDbType.RefCursor }, + { typeof(bool), OracleDbType.Boolean }, + { typeof(int), OracleDbType.Int32 }, + { typeof(uint), OracleDbType.Int32 }, + { typeof(short), OracleDbType.Int16 }, + { typeof(ushort), OracleDbType.Int16 }, + { typeof(long), OracleDbType.Int64 }, + { typeof(ulong), OracleDbType.Int64 }, + { typeof(byte), OracleDbType.Byte }, + { typeof(sbyte), OracleDbType.Byte }, + { typeof(float), OracleDbType.Single }, + { typeof(double), OracleDbType.Double }, + { typeof(decimal), OracleDbType.Decimal } + }; } } \ No newline at end of file diff --git a/Activities/Database/UiPath.Database/ParameterInfo.cs b/Activities/Database/UiPath.Database/ParameterInfo.cs new file mode 100644 index 00000000..484a68ac --- /dev/null +++ b/Activities/Database/UiPath.Database/ParameterInfo.cs @@ -0,0 +1,17 @@ +using System; +using System.Activities; + +namespace UiPath.Database +{ + /// + /// Information about a parameter that will bind to a query + /// + public class ParameterInfo + { + public object Value { get; set; } + + public Type Type { get; set; } + + public ArgumentDirection Direction { get; set; } + } +}