Просмотр исходного кода

2005-07-08 Daniel Morgan <[email protected]>

	* System.Data.OracleClient/OracleParameter.cs
	* System.Data.OracleClient/OracleCommand.cs
	* System.Data.OracleClient/OracleDataReader.cs: support
	Output parameters.  Return and InputOutput are TODO.  Input
	already supported.  TODO for handling NULL indicator on Output params.

	* System.Data.OracleClient/OracleConnection.cs: remove
	compile warning

	* Test/TestOracleClient.cs: add tests for out parameters

svn path=/trunk/mcs/; revision=47076
Daniel Morgan 20 лет назад
Родитель
Сommit
586b31fa86

+ 13 - 0
mcs/class/System.Data.OracleClient/ChangeLog

@@ -1,3 +1,16 @@
+2005-07-08  Daniel Morgan <[email protected]>
+
+	* System.Data.OracleClient/OracleParameter.cs
+	* System.Data.OracleClient/OracleCommand.cs
+	* System.Data.OracleClient/OracleDataReader.cs: support
+	Output parameters.  Return and InputOutput are TODO.  Input
+	already supported.  TODO for handling NULL indicator on Output params.
+
+	* System.Data.OracleClient/OracleConnection.cs: remove
+	compile warning
+
+	* Test/TestOracleClient.cs: add tests for out parameters
+
 2005-03-06  Daniel Morgan <[email protected]>
 
 	- Applied patch by Hubert FONGARNAND 

+ 4 - 2
mcs/class/System.Data.OracleClient/System.Data.OracleClient.Oci/OciDefineHandle.cs

@@ -45,7 +45,9 @@ namespace System.Data.OracleClient.Oci
 
 		// Oracle defines the LONG VARCHAR have a size of 2 to the 31 power - 5
 		// maybe this should settable via a config file for System.Data.OracleClient.dll
-		// see DefineLong
+		// see DefineLong.  Or if the a truncate sql error occurs, then retry?
+		// or maybe allocate the memory yourself and then let oracle return error more data,
+		// then try to get some more data
 		internal static int LongVarCharMaxValue = (int) Int16.MaxValue - 5;
 
 		OciErrorHandle errorHandle;
@@ -464,7 +466,7 @@ namespace System.Data.OracleClient.Oci
 		}
 
 		[MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
-		public DateTime UnpackDate ()
+		internal DateTime UnpackDate ()
 		{
 			byte century = Marshal.ReadByte (value, 0);
 			byte year = Marshal.ReadByte (value, 1);

+ 17 - 0
mcs/class/System.Data.OracleClient/System.Data.OracleClient/OracleCommand.cs

@@ -251,8 +251,21 @@ namespace System.Data.OracleClient {
 			return cmd;
 		}
 
+		internal void GetOutParameters () 
+		{
+			if (Parameters.Count > 0) {
+				foreach (OracleParameter parm in Parameters) {
+					if (parm.Direction != ParameterDirection.Input) {
+						parm.Update (this);
+					}
+				}
+			}
+		}
+
 		internal void CloseDataReader ()
 		{
+			GetOutParameters ();
+		
 			Connection.DataReader = null;
 			if ((behavior & CommandBehavior.CloseConnection) != 0)
 				Connection.Close ();
@@ -276,6 +289,8 @@ namespace System.Data.OracleClient {
 			else
 				statement.ExecuteQuery ();
 
+			GetOutParameters ();
+
 			int rowsAffected = statement.GetAttributeInt32 (OciAttributeType.RowCount, ErrorHandle);
 		
 			return rowsAffected;
@@ -368,6 +383,7 @@ namespace System.Data.OracleClient {
 							break;
 						}
 					}
+					GetOutParameters ();
 				}
 
 				return output;
@@ -480,6 +496,7 @@ namespace System.Data.OracleClient {
 					}
 					else
 						output = DBNull.Value;
+					GetOutParameters ();
 				}
 			}
 			finally {

+ 1 - 1
mcs/class/System.Data.OracleClient/System.Data.OracleClient/OracleConnection.cs

@@ -279,7 +279,7 @@ namespace System.Data.OracleClient
 		{
 			byte[] buffer = new Byte[bufflen];
 
-			int st = OciCalls.OCINlsGetInfo (handle, ErrorHandle, 
+			OciCalls.OCINlsGetInfo (handle, ErrorHandle, 
 				ref buffer, bufflen, (ushort) item);
 
 			// Get length of returned string

+ 2 - 2
mcs/class/System.Data.OracleClient/System.Data.OracleClient/OracleDataReader.cs

@@ -114,10 +114,10 @@ namespace System.Data.OracleClient {
 		#region Methods
 
 		public void Close ()
-		{
-			statement.Dispose();
+		{	
 			if (!isClosed) 
 				command.CloseDataReader ();
+			statement.Dispose();
 			isClosed = true;
 		}
 

+ 236 - 36
mcs/class/System.Data.OracleClient/System.Data.OracleClient/OracleParameter.cs

@@ -45,11 +45,17 @@ namespace System.Data.OracleClient {
 		DbType dbType = DbType.AnsiString;
 		int offset = 0;
 		bool sizeSet = false;
-		object value = null;
+		object value = DBNull.Value;
 		OciLobLocator lobLocator = null;  // only if Blob or Clob
+		IntPtr bindOutValue = IntPtr.Zero;
 
 		OracleParameterCollection container = null;
 		OciBindHandle bindHandle;
+		OciErrorHandle errorHandle;
+		OracleConnection connection;
+
+		int indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
+		int bindSize = 0;
 
 		#endregion // Fields
 
@@ -214,12 +220,25 @@ namespace System.Data.OracleClient {
 
 		private void AssertSizeIsSet ()
 		{
-			if (!sizeSet)
-				throw new Exception ("Size must be set.");
+			switch (ociType) {
+			case OciDataType.VarChar2:
+			case OciDataType.String:
+			case OciDataType.VarChar:
+			case OciDataType.Char:
+			case OciDataType.CharZ:
+			case OciDataType.OciString:
+				if (!sizeSet)
+					throw new Exception ("Size must be set.");
+				break;
+			default:
+				break;
+			}
 		}
 
 		internal void Bind (OciStatementHandle statement, OracleConnection connection) 
 		{
+			errorHandle = connection.ErrorHandle;
+
 			if (bindHandle == null)
 				bindHandle = new OciBindHandle ((OciHandle) statement);
 
@@ -230,15 +249,74 @@ namespace System.Data.OracleClient {
 			if (!sizeSet)
 				size = InferSize ();
 
+			bindSize = size;
 			byte[] bytes = null;
+			object v = value;
 			int status = 0;
-			int indicator = 0;
 			OciDataType bindType = ociType;
 			IntPtr bindValue = IntPtr.Zero;
-			int bindSize = size;
 			int rsize = 0;
 
-			if (value == DBNull.Value) {
+			// TODO: handle InputOutput and Return parameters
+			if (direction == ParameterDirection.Output) {
+				// TODO: need to figure out how OracleParameter
+				//       which uses OciBindHandle to share code
+				//       with OciDefineHandle
+				switch(ociType) {
+					case OciDataType.VarChar2:
+					case OciDataType.String:
+					case OciDataType.VarChar:
+					case OciDataType.Char:
+					case OciDataType.CharZ:
+					case OciDataType.OciString:
+						bindType = OciDataType.Char;
+						bindSize = size * 2;
+						bindOutValue = Marshal.AllocHGlobal (bindSize);
+						break;
+					case OciDataType.RowIdDescriptor:
+						size = 10;
+						bindType = OciDataType.Char;
+						bindSize = size * 2;
+						bindOutValue = Marshal.AllocHGlobal (bindSize);
+						break;
+					case OciDataType.Date:
+						bindSize = 7;
+						bindType = OciDataType.Date;
+						bindOutValue = Marshal.AllocHGlobal (bindSize);
+						break;
+					case OciDataType.Number:
+						bindSize = 22;
+						bindType = OciDataType.Char;
+						bindOutValue = Marshal.AllocHGlobal (bindSize);
+						break;
+					case OciDataType.Long:
+					case OciDataType.LongVarChar:
+						// LAMESPEC: you don't know size until you get it;
+						// therefore, you must allocate an insane size
+						// see OciDefineHandle
+						bindSize = OciDefineHandle.LongVarCharMaxValue;
+						bindOutValue = Marshal.AllocHGlobal (bindSize);
+						bindType = OciDataType.LongVarChar;
+						break;
+					case OciDataType.Blob:
+					case OciDataType.Clob:
+						bindSize = -1;
+						lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
+						if (lobLocator == null) {
+							OciErrorInfo info = connection.ErrorHandle.HandleError ();
+							throw new OracleException (info.ErrorCode, info.ErrorMessage);
+						}
+						value = lobLocator.Handle;
+						lobLocator.ErrorHandle = connection.ErrorHandle;
+						lobLocator.Service = statement.Service;
+						break;
+					default:
+						// define other types
+						throw new NotImplementedException ();
+				}
+				bindValue = bindOutValue;
+			}
+			else if (v == DBNull.Value || v == null) {
 				indicator = 0;
 				bindType = OciDataType.VarChar2;
 				bindSize = 0;
@@ -252,18 +330,18 @@ namespace System.Data.OracleClient {
 
 					string sDate = "";
 					DateTime dt = DateTime.MinValue;
-					if (value is String) {
-						sDate = (string) value;
+					if (v is String) {
+						sDate = (string) v;
 						dt = DateTime.Parse (sDate);
 					}
-					else if (value is DateTime)
-						dt = (DateTime) value;
-					else if (value is OracleString) {
-						sDate = (string) value;
+					else if (v is DateTime)
+						dt = (DateTime) v;
+					else if (v is OracleString) {
+						sDate = (string) v;
 						dt = DateTime.Parse (sDate);
 					}
-					else if (value is OracleDateTime) {
-						OracleDateTime odt = (OracleDateTime) value;
+					else if (v is OracleDateTime) {
+						OracleDateTime odt = (OracleDateTime) v;
 						dt = (DateTime) odt.Value;
 					}
 					else
@@ -284,32 +362,32 @@ namespace System.Data.OracleClient {
 					bindSize = sDate.Length;
 				}
 				else if (oracleType == OracleType.Blob) {
-					bytes = (byte[]) value;
+					bytes = (byte[]) v;
 					bindType = OciDataType.LongRaw;
 					bindSize = bytes.Length;
 				}
 				else if (oracleType == OracleType.Clob) {
-					string v = (string) value;
+					string sv = (string) v;
 					rsize = 0;
 			
 					// Get size of buffer
-					OciCalls.OCIUnicodeToCharSet (statement.Parent, null, v, out rsize);
+					OciCalls.OCIUnicodeToCharSet (statement.Parent, null, sv, out rsize);
 			
 					// Fill buffer
 					bytes = new byte[rsize];
-					OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, v, out rsize);
+					OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, sv, out rsize);
 
 					bindType = OciDataType.Long;
 					bindSize = bytes.Length;
 				}
 				else if (oracleType == OracleType.Raw) {
-					byte[] val = value as byte[];
+					byte[] val = v as byte[];
 					bindValue = Marshal.AllocHGlobal (val.Length);
 					Marshal.Copy (val, 0, bindValue, val.Length);
 					bindSize = val.Length;
 				}
 				else {
-					string svalue = value.ToString ();
+					string svalue = v.ToString ();
 					rsize = 0;
 			
 					// Get size of buffer
@@ -321,7 +399,7 @@ namespace System.Data.OracleClient {
 
 					//bindValue = Marshal.StringToHGlobalAnsi (value.ToString ());
 					bindType = OciDataType.VarChar2;
-					bindSize = value.ToString ().Length;
+					bindSize = v.ToString ().Length;
 				}
 			}
 
@@ -415,10 +493,50 @@ namespace System.Data.OracleClient {
 			}
 		}
 
-		[MonoTODO ("different size depending on type.")]
 		private int InferSize ()
 		{
-			return value.ToString ().Length;
+			int newSize = 0;
+			
+			switch (ociType) {
+			case OciDataType.VarChar2:
+			case OciDataType.String:
+			case OciDataType.VarChar:
+			case OciDataType.Char:
+			case OciDataType.CharZ:
+			case OciDataType.OciString:
+			case OciDataType.Long:
+			case OciDataType.LongVarChar:
+				if (value == null || value == DBNull.Value)
+					newSize = 0;
+				else
+					newSize = value.ToString ().Length;
+				break;
+			case OciDataType.RowIdDescriptor:
+				newSize = 10;
+				break;
+			case OciDataType.Integer:
+			case OciDataType.Number:
+			case OciDataType.Float:
+				newSize = 22;
+				break;
+			case OciDataType.Date:
+				newSize = 7;
+				break;	
+			case OciDataType.Blob:
+			case OciDataType.Clob:
+				newSize = -1;
+				break;					
+			default:
+				if (value == null || value == DBNull.Value)
+					newSize = 0;
+				else
+					newSize = value.ToString ().Length;
+				break;
+			}
+
+			sizeSet = true;
+
+			return newSize;
 		}
 
 		private void SetDbType (DbType type)
@@ -496,6 +614,9 @@ namespace System.Data.OracleClient {
 			switch (type) {
 			case OracleType.BFile:
 			case OracleType.Blob:
+				dbType = DbType.Binary;
+				ociType = OciDataType.Blob;
+				break;
 			case OracleType.LongRaw:
 			case OracleType.Raw:
 				dbType = DbType.Binary;
@@ -503,13 +624,16 @@ namespace System.Data.OracleClient {
 				break;
 			case OracleType.Byte:
 				dbType = DbType.Byte;
-				ociType = OciDataType.Integer;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.Char:
-				dbType = DbType.AnsiStringFixedLength;
+				dbType = DbType.AnsiString;
 				ociType = OciDataType.Char;
 				break;
 			case OracleType.Clob:
+				dbType = DbType.AnsiString;
+				ociType = OciDataType.Clob;
+				break;
 			case OracleType.LongVarChar:
 			case OracleType.RowId:
 			case OracleType.VarChar:
@@ -518,32 +642,32 @@ namespace System.Data.OracleClient {
 				break;
 			case OracleType.Cursor:
 			case OracleType.IntervalDayToSecond:
-				dbType = DbType.Object;
-				ociType = OciDataType.Blob;
+				dbType = DbType.AnsiStringFixedLength;
+				ociType = OciDataType.Char;
 				break;
 			case OracleType.DateTime:
 			case OracleType.Timestamp:
 			case OracleType.TimestampLocal:
 			case OracleType.TimestampWithTZ:
 				dbType = DbType.DateTime;
-				ociType = OciDataType.Char;
+				ociType = OciDataType.Date;
 				break;
 			case OracleType.Double:
 				dbType = DbType.Double;
-				ociType = OciDataType.Float;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.Float:
 				dbType = DbType.Single;
-				ociType = OciDataType.Float;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.Int16:
 				dbType = DbType.Int16;
-				ociType = OciDataType.Integer;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.Int32:
 			case OracleType.IntervalYearToMonth:
 				dbType = DbType.Int32;
-				ociType = OciDataType.Integer;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.NChar:
 				dbType = DbType.StringFixedLength;
@@ -552,7 +676,7 @@ namespace System.Data.OracleClient {
 			case OracleType.NClob:
 			case OracleType.NVarChar:
 				dbType = DbType.String;
-				ociType = OciDataType.VarChar;
+				ociType = OciDataType.Char;
 				break;
 			case OracleType.Number:
 				dbType = DbType.VarNumeric;
@@ -560,15 +684,15 @@ namespace System.Data.OracleClient {
 				break;
 			case OracleType.SByte:
 				dbType = DbType.SByte;
-				ociType = OciDataType.Integer;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.UInt16:
 				dbType = DbType.UInt16;
-				ociType = OciDataType.Integer;
+				ociType = OciDataType.Number;
 				break;
 			case OracleType.UInt32:
 				dbType = DbType.UInt32;
-				ociType = OciDataType.Integer;
+				ociType = OciDataType.Number;
 				break;
 			default:
 				throw new ArgumentException (exception);
@@ -582,6 +706,82 @@ namespace System.Data.OracleClient {
 			return ParameterName;
 		}
 
+		internal void Update (OracleCommand cmd) 
+		{
+			// used to update the parameter value
+			// for Output, the output of InputOutput, and Return parameters
+
+			// TODO: handle NULL values by using the indicator variable
+			//       referenced in call to OCIBindByName
+			value = DBNull.Value;
+
+			byte[] buffer = null;
+			object tmp = null;
+
+			switch (ociType) {
+			case OciDataType.VarChar2:
+			case OciDataType.String:
+			case OciDataType.VarChar:
+			case OciDataType.Char:
+			case OciDataType.CharZ:
+			case OciDataType.OciString:
+			case OciDataType.RowIdDescriptor:
+				buffer = new byte [Size];
+				Marshal.Copy (bindOutValue, buffer, 0, Size);
+				
+				// Get length of returned string
+				int 	rsize = 0;
+				IntPtr	env = cmd.Connection.Environment;
+				OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
+			
+				// Get string
+				StringBuilder ret = new StringBuilder(rsize);
+				OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
+	
+				value = ret.ToString ();
+				break;
+			case OciDataType.Integer:
+			case OciDataType.Number:
+			case OciDataType.Float:
+				tmp = Marshal.PtrToStringAnsi (bindOutValue, bindSize);
+				if (tmp != null)
+					value = Decimal.Parse (String.Copy ((string) tmp));
+				break;
+			case OciDataType.Date:
+				value = UnpackDate (bindOutValue);
+				break;	
+			case OciDataType.Blob:
+			case OciDataType.Clob:
+				OracleLob lob = new OracleLob (lobLocator, ociType);
+				lob.connection = connection;
+				value = lob;
+				break;
+			default:
+				throw new NotImplementedException ();
+			}
+		}
+
+		// copied from OciDefineHandle
+		[MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
+		internal DateTime UnpackDate (IntPtr dateValue)
+		{
+			byte century = Marshal.ReadByte (dateValue, 0);
+			byte year = Marshal.ReadByte (dateValue, 1);
+			byte month = Marshal.ReadByte (dateValue, 2);
+			byte day = Marshal.ReadByte (dateValue, 3);
+			byte hour = Marshal.ReadByte (dateValue, 4);
+			byte minute = Marshal.ReadByte (dateValue, 5);
+			byte second = Marshal.ReadByte (dateValue, 6);
+
+			return new DateTime ((century - 100) * 100 + (year - 100),
+						month,
+						day,
+						hour - 1,
+						minute - 1,
+						second - 1);
+
+		}
+
 		#endregion // Methods
 
 		internal sealed class OracleParameterConverter : ExpandableObjectConverter

+ 184 - 3
mcs/class/System.Data.OracleClient/Test/TestOracleClient.cs

@@ -863,6 +863,175 @@ namespace Test.OracleClient
 			cmd3.ExecuteNonQuery ();
 		}
 
+		static void OutParmTest1 (OracleConnection con) 
+		{
+			// test stored procedure with 2 parameters
+			// 1. input varchar2
+			// 2. output varchar
+
+			OracleCommand cmd2 = null;
+			Console.WriteLine("  Drop procedure SP_OUTPUTPARMTEST1...");
+			try {
+				cmd2 = con.CreateCommand ();
+				cmd2.CommandText = "DROP PROCEDURE SP_OUTPUTPARMTEST1";
+				cmd2.ExecuteNonQuery ();
+			}
+			catch(OracleException oe1) {
+				// ignore if table already exists
+			}
+			
+			Console.WriteLine("  Create stored procedure SP_OUTPUTPARMTEST1...");
+			// stored procedure concatenates strings
+			cmd2.CommandText = 
+				"CREATE OR REPLACE PROCEDURE SP_TESTOUTPARM1(parm1 IN VARCHAR2,parm2 OUT VARCHAR2) " +
+				"IS " +
+				"BEGIN " +
+				"	parm2 := 'one' || parm1 || 'three';" +
+				"END;";
+
+			cmd2.ExecuteNonQuery ();
+
+			Console.WriteLine("  COMMIT...");
+			cmd2.CommandText = "COMMIT";
+			cmd2.ExecuteNonQuery ();
+
+			Console.WriteLine("  Call stored procedure SP_TESTOUTPARM1 with two parameters...");
+			OracleCommand cmd3 = con.CreateCommand ();
+			cmd3.CommandType = CommandType.Text;
+			cmd3.CommandText = 
+				"BEGIN " +
+				"	SP_TESTOUTPARM1(:p1, :p2);" +
+				"END;";
+			OracleParameter myParameter1 = new OracleParameter("p1", OracleType.VarChar);
+			myParameter1.Value = "two";
+			myParameter1.Size = 4;
+			myParameter1.Direction = ParameterDirection.Input;
+		
+			OracleParameter myParameter2 = new OracleParameter("p2", OracleType.VarChar);
+			myParameter2.Size = 12;
+			myParameter2.Direction = ParameterDirection.Output;
+
+			cmd3.Parameters.Add (myParameter1);
+			cmd3.Parameters.Add (myParameter2);
+
+			cmd3.ExecuteNonQuery ();
+			string outValue = (string) myParameter2.Value;
+			Console.WriteLine ("    Out Value should be: onetwothree");
+			Console.WriteLine ("    Out Value: " + outValue);
+		}
+
+		static void OutParmTest2 (OracleConnection con) 
+		{
+			// test stored procedure with 2 parameters
+			// 1. input number(18,2)
+			// 2. output number(18,2)
+
+			OracleCommand cmd2 = null;
+			Console.WriteLine("  Drop procedure SP_OUTPUTPARMTEST2...");
+			try {
+				cmd2 = con.CreateCommand ();
+				cmd2.CommandText = "DROP PROCEDURE SP_OUTPUTPARMTEST2";
+				cmd2.ExecuteNonQuery ();
+			}
+			catch(OracleException oe1) {
+				// ignore if table already exists
+			}
+			
+			Console.WriteLine("  Create stored procedure SP_OUTPUTPARMTEST2...");
+
+			// stored procedure addes two numbers
+			cmd2.CommandText = 
+				"CREATE OR REPLACE PROCEDURE SP_TESTOUTPARM2(parm1 IN NUMBER,parm2 OUT NUMBER) " +
+				"IS " +
+				"BEGIN " +
+				"	parm2 := parm1 + 3; " +
+				"END;";
+
+			cmd2.ExecuteNonQuery ();
+
+			Console.WriteLine("  COMMIT...");
+			cmd2.CommandText = "COMMIT";
+			cmd2.ExecuteNonQuery ();
+
+			Console.WriteLine("  Call stored procedure SP_TESTOUTPARM2 with two parameters...");
+			OracleCommand cmd3 = con.CreateCommand ();
+			cmd3.CommandType = CommandType.Text;
+			cmd3.CommandText = 
+				"BEGIN " +
+				"	SP_TESTOUTPARM2(:p1, :p2);" +
+				"END;";
+			OracleParameter myParameter1 = new OracleParameter("p1", OracleType.Number);
+			myParameter1.Value = 2;
+			myParameter1.Direction = ParameterDirection.Input;
+		
+			OracleParameter myParameter2 = new OracleParameter("p2", OracleType.Number);
+			myParameter2.Direction = ParameterDirection.Output;
+
+			cmd3.Parameters.Add (myParameter1);
+			cmd3.Parameters.Add (myParameter2);
+
+			cmd3.ExecuteNonQuery ();
+			decimal outValue = (decimal) myParameter2.Value;
+			Console.WriteLine ("    Out Value should be: 5");
+			Console.WriteLine ("    Out Value: {0}", outValue);
+		}
+
+		static void OutParmTest3 (OracleConnection con) 
+		{
+			// test stored procedure with 2 parameters
+			// 1. input date
+			// 2. output date
+
+			OracleCommand cmd2 = null;
+			Console.WriteLine("  Drop procedure SP_OUTPUTPARMTEST3...");
+			try {
+				cmd2 = con.CreateCommand ();
+				cmd2.CommandText = "DROP PROCEDURE SP_OUTPUTPARMTEST3";
+				cmd2.ExecuteNonQuery ();
+			}
+			catch(OracleException oe1) {
+				// ignore if table already exists
+			}
+			
+			Console.WriteLine("  Create stored procedure SP_OUTPUTPARMTEST3...");
+
+			// stored procedure adds 3 days to date 
+			cmd2.CommandText = 
+				"CREATE OR REPLACE PROCEDURE SP_TESTOUTPARM3(parm1 IN DATE,parm2 OUT DATE) " +
+				"IS " +
+				"BEGIN " +
+				"	parm2 := parm1 + 3; " +
+				"END;";
+
+			cmd2.ExecuteNonQuery ();
+
+			Console.WriteLine("  COMMIT...");
+			cmd2.CommandText = "COMMIT";
+			cmd2.ExecuteNonQuery ();
+
+			Console.WriteLine("  Call stored procedure SP_TESTOUTPARM3 with two parameters...");
+			OracleCommand cmd3 = con.CreateCommand ();
+			cmd3.CommandType = CommandType.Text;
+			cmd3.CommandText = 
+				"BEGIN " +
+				"	SP_TESTOUTPARM3(:p1, :p2);" +
+				"END;";
+			OracleParameter myParameter1 = new OracleParameter("p1", OracleType.DateTime);
+			myParameter1.Value = new DateTime(2004,12,15);
+			myParameter1.Direction = ParameterDirection.Input;
+		
+			OracleParameter myParameter2 = new OracleParameter("p2", OracleType.DateTime);
+			myParameter2.Direction = ParameterDirection.Output;
+
+			cmd3.Parameters.Add (myParameter1);
+			cmd3.Parameters.Add (myParameter2);
+
+			cmd3.ExecuteNonQuery ();
+			DateTime outValue = (DateTime) myParameter2.Value;
+			Console.WriteLine ("    Out Value should be: 2004-12-18");
+			Console.WriteLine ("    Out Value: {0}", outValue.ToString ("yyyy-mm-dd"));
+		}
+
 		static void ShowConnectionProperties (OracleConnection con) 
 		{
 			try {
@@ -1114,6 +1283,18 @@ namespace Test.OracleClient
 			ReadSimpleTest(con1, "SELECT * FROM MONO_TEST_TABLE2");
 			Console.WriteLine ("Stored Proc Test 2 END...");
 
+			Console.WriteLine ("Out Parameter and PL/SQL Block Test 1 BEGIN...");
+			OutParmTest1 (con1); 
+			Console.WriteLine ("Out Parameter and PL/SQL Block Test 1 END...");
+
+			Console.WriteLine ("Out Parameter and PL/SQL Block Test 2 BEGIN...");
+			OutParmTest2 (con1); 
+			Console.WriteLine ("Out Parameter and PL/SQL Block Test 2 END...");
+
+			Console.WriteLine ("Out Parameter and PL/SQL Block Test 3 BEGIN...");
+			OutParmTest3 (con1); 
+			Console.WriteLine ("Out Parameter and PL/SQL Block Test 3 END...");
+
 			Wait ("");
 
 			Console.WriteLine ("Null Aggregate Warning BEGIN test...");
@@ -1124,9 +1305,9 @@ namespace Test.OracleClient
 			con1.Close ();
 			Console.WriteLine("Closed.");
 
-			conStr = conStr + ";pooling=true;min pool size=4;max pool size=" + MAX_CONNECTIONS.ToString ();
-			ConnectionPoolingTest1 ();
-			ConnectionPoolingTest2 ();
+			//conStr = conStr + ";pooling=true;min pool size=4;max pool size=" + MAX_CONNECTIONS.ToString ();
+			//ConnectionPoolingTest1 ();
+			//ConnectionPoolingTest2 ();
 
 			Console.WriteLine("Done.");
 		}