Fix for bug 77592 (memory leak in OracleParameter)
[mono.git] / mcs / class / System.Data.OracleClient / System.Data.OracleClient / OracleParameter.cs
index adb28261bbbe84572f20bb92706c219fcc207c54..64208b1f441b6878df7327f9963ae3ea889e3150 100644 (file)
@@ -9,8 +9,12 @@
 //
 // Authors: 
 //    Tim Coleman <tim@timcoleman.com>
+//    Daniel Moragn <danielmorgan@verizon.net>
+//    Hubert FONGARNAND <informatique.internet@fiducial.fr>
 //
 // Copyright (C) Tim Coleman , 2003
+// Copyright (C) Daniel Morgan, 2005
+// Copyright (C) Hubert FONGARNAND, 2005
 //
 // Licensed under the MIT/X11 License.
 //
@@ -19,55 +23,111 @@ using System;
 using System.Collections;
 using System.ComponentModel;
 using System.Data;
+using System.Data.SqlTypes;
 using System.Data.OracleClient.Oci;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Text;
 
 namespace System.Data.OracleClient {
+       [TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
        public sealed class OracleParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
        {
                #region Fields
 
-               DbType dbType = DbType.AnsiString;
+               string name;
+               OracleType oracleType = OracleType.VarChar;
+               OciDataType ociType;
+               int size;
                ParameterDirection direction = ParameterDirection.Input;
-               bool isNullable = false;
+               bool isNullable;
+               byte precision;
+               byte scale;
+               string srcColumn;
+               DataRowVersion srcVersion;
+               DbType dbType = DbType.AnsiString;
                int offset = 0;
-               OracleType oracleType = OracleType.VarChar;
-               string name;
-               byte precision = 0x0;
-               byte scale = 0x0;
-               int size = 0;
                bool sizeSet = false;
-               string srcColumn = String.Empty;
-               DataRowVersion srcVersion = DataRowVersion.Current;
-               object value = null;
+               object value = DBNull.Value;
+               OciLobLocator lobLocator = null;  // only if Blob or Clob
+               IntPtr bindOutValue = IntPtr.Zero;
+               OciDateTimeDescriptor dateTimeDesc = null;
+               IntPtr cursor = IntPtr.Zero;
 
                OracleParameterCollection container = null;
+               OciBindHandle bindHandle;
+               OciErrorHandle errorHandle;
+               OracleConnection connection;
+               byte[] bytes = null;
+               IntPtr bindValue = IntPtr.Zero;
+               bool useRef = false;
+               OciDataType bindType;
+
+               short indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
+               int bindSize = 0;
+               uint position = 0;
 
                #endregion // Fields
 
                #region Constructors
 
+               // constructor for cloning the object
+               internal OracleParameter (OracleParameter value) {
+                       this.name = value.name;
+                       this.oracleType = value.oracleType;
+                       this.ociType = value.ociType;
+                       this.size = value.size;
+                       this.direction = value.direction;
+                       this.isNullable = value.isNullable;
+                       this.precision = value.precision;
+                       this.scale = value.scale;
+                       this.srcColumn = value.srcColumn;
+                       this.srcVersion = value.srcVersion;
+                       this.dbType = value.dbType;
+                       this.offset = value.offset;
+                       this.sizeSet = value.sizeSet;
+                       this.value = value.value;
+                       this.lobLocator = value.lobLocator;
+               }
+
                public OracleParameter ()
+                       : this (String.Empty, OracleType.VarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
                {
                }
 
                public OracleParameter (string name, object value)
                {
+                       this.name = name;
+                       this.value = value;
+                       SourceVersion = DataRowVersion.Current;
+                       InferOracleType (value);
                }
 
                public OracleParameter (string name, OracleType dataType)
+                       : this (name, dataType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
                {
                }
 
                public OracleParameter (string name, OracleType dataType, int size)
+                       : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
                {
                }
 
                public OracleParameter (string name, OracleType dataType, int size, string srcColumn)
+                       : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
                {
                }
 
                public OracleParameter (string name, OracleType dataType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
                {
+                       this.name = name;
+                       this.size = size;
+                       this.value = value;
+
+                       OracleType = dataType;
+                       Direction = direction;
+                       SourceColumn = srcColumn;
+                       SourceVersion = srcVersion;
                }
 
                #endregion // Constructors
@@ -79,46 +139,63 @@ namespace System.Data.OracleClient {
                        set { container = value; }
                }
 
+               [Browsable (false)]
+               [RefreshProperties (RefreshProperties.All)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                public DbType DbType {
                        get { return dbType; }
                        set { SetDbType (value); }
                }
 
+               [DefaultValue (ParameterDirection.Input)]
+               [RefreshProperties (RefreshProperties.All)]
                public ParameterDirection Direction {
                        get { return direction; }
                        set { direction = value; }
                }
 
+               [Browsable (false)]
+               [DesignOnly (true)]
+               [DefaultValue (false)]
+               [EditorBrowsable (EditorBrowsableState.Never)]
                public bool IsNullable {
                        get { return isNullable; }
                        set { isNullable = value; }
                }
 
+               [DefaultValue (0)]
+               [Browsable (false)]
                public int Offset {
                        get { return offset; }
                        set { offset = value; }
                }
 
+               [DefaultValue (OracleType.VarChar)]
+               [RefreshProperties (RefreshProperties.All)]
                public OracleType OracleType {
                        get { return oracleType; }
                        set { SetOracleType (value); }
                }
                
+               [DefaultValue ("")]
                public string ParameterName {
                        get { return name; }
                        set { name = value; }
                }
 
+               [DefaultValue (0)]
                public byte Precision {
                        get { return precision; }
                        set { /* NO EFFECT*/ }
                }
 
+               [DefaultValue (0)]
                public byte Scale {
-                       get { return precision; }
+                       get { return scale; }
                        set { /* NO EFFECT*/ }
                }
 
+               [DefaultValue (0)]
                public int Size {
                        get { return size; }
                        set { 
@@ -127,16 +204,21 @@ namespace System.Data.OracleClient {
                        }
                }
 
+               [DefaultValue ("")]
                public string SourceColumn {
                        get { return srcColumn; }
                        set { srcColumn = value; }
                }
 
+               [DefaultValue ("Current")]
                public DataRowVersion SourceVersion {
                        get { return srcVersion; }
                        set { srcVersion = value; }
                }
 
+               [DefaultValue (null)]
+               [RefreshProperties (RefreshProperties.All)]
+               [TypeConverter (typeof(StringConverter))]
                public object Value {
                        get { return this.value; }
                        set { this.value = value; }
@@ -146,10 +228,650 @@ namespace System.Data.OracleClient {
 
                #region Methods
 
-               [MonoTODO]
+               private void AssertSizeIsSet ()
+               {
+                       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 con, uint pos)
+               {
+                       connection = con;
+
+                       errorHandle = connection.ErrorHandle;
+
+                       if (bindHandle == null)
+                               bindHandle = new OciBindHandle ((OciHandle) statement);
+
+                       IntPtr tmpHandle = bindHandle.Handle;
+
+                       position = pos;
+
+                       if (Direction != ParameterDirection.Input)
+                               AssertSizeIsSet ();
+                       if (!sizeSet)
+                               size = InferSize ();
+
+                       bindSize = size;
+                       object v = value;
+                       int status = 0;
+                       bindType = ociType;
+                       int rsize = 0;
+
+                       bool isnull = false;
+                       if (direction == ParameterDirection.Input || direction == ParameterDirection.InputOutput) {
+                               try {
+                                       Type nullable = v.GetType ().GetInterface ("System.Data.SqlTypes.INullable", false);
+                                       if (nullable != null) {
+                                               INullable mynullable = (INullable)v;
+                                               isnull = mynullable.IsNull;
+                                       }
+                               }
+                               catch(Exception e) {
+                                       System.IO.TextWriter.Null.WriteLine(e.Message);
+                               }
+                       }
+
+                       // 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);
+                                               bindValue = bindOutValue;
+                                               break;
+                                       case OciDataType.RowIdDescriptor:
+                                               size = 10;
+                                               bindType = OciDataType.Char;
+                                               bindSize = size * 2;
+                                               bindOutValue = Marshal.AllocHGlobal (bindSize);
+                                               bindValue = bindOutValue;
+                                               break;
+                                       case OciDataType.Date:
+                                               bindSize = 7;
+                                               bindType = OciDataType.Date;
+                                               bindOutValue = Marshal.AllocHGlobal (bindSize);
+                                               bindValue = bindOutValue;
+                                               break;
+                                       case OciDataType.TimeStamp:
+                                               dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
+                                               if (dateTimeDesc == null) {
+                                                       OciErrorInfo info = connection.ErrorHandle.HandleError ();
+                                                       throw new OracleException (info.ErrorCode, info.ErrorMessage);
+                                               }
+                                               dateTimeDesc.ErrorHandle = connection.ErrorHandle;
+                                               bindSize = 11;
+                                               bindType = OciDataType.TimeStamp;
+                                               bindOutValue = dateTimeDesc.Handle;
+                                               bindValue = dateTimeDesc.Handle;
+                                               useRef = true;
+                                               break;
+                                       case OciDataType.Number:
+                                               bindSize = 22;
+                                               bindType = OciDataType.Char;
+                                               bindOutValue = Marshal.AllocHGlobal (bindSize);
+                                               bindValue = bindOutValue;
+                                               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;
+                                               bindValue = bindOutValue;
+                                               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);
+                                               }
+                                               bindOutValue = lobLocator.Handle;
+                                               bindValue = lobLocator.Handle;
+                                               lobLocator.ErrorHandle = connection.ErrorHandle;
+                                               lobLocator.Service = statement.Service;
+                                               useRef = true;
+                                               break;                                  
+                                       case OciDataType.RSet: // REF CURSOR
+                                               cursor = IntPtr.Zero;
+                                               OciCalls.OCIHandleAlloc (connection.Environment,
+                                                       out cursor,
+                                                       OciHandleType.Statement,
+                                                       0,
+                                                       IntPtr.Zero);
+                                                       
+                                               bindSize = 0;
+                                               bindType = OciDataType.RSet;
+                                               break;                                  
+                                       default:
+                                               // define other types
+                                               throw new NotImplementedException ();
+                               }
+                               bindValue = bindOutValue;
+                       }
+                       else if ((v == DBNull.Value || v == null || isnull == true) && direction == ParameterDirection.Input) {
+                               indicator = 0;
+                               bindType = OciDataType.VarChar2;
+                               bindSize = 0;
+                       }
+                       else {
+                               // TODO: do other data types and oracle data types
+                               // should I be using IConvertible to convert?
+                               string sDate = "";
+                               DateTime dt = DateTime.MinValue;
+                               if (oracleType == OracleType.Timestamp){
+                                       bindType = OciDataType.TimeStamp;
+                                       bindSize = 11;
+                                       dt = DateTime.MinValue;
+                                       sDate = "";
+                                       if (v is String){
+                                               sDate = (string) v;
+                                               dt = DateTime.Parse (sDate);
+                                       }
+                                       else if (v is DateTime)
+                                               dt = (DateTime) v;
+                                       else if (v is OracleString){
+                                               sDate = (string) v;
+                                               dt = DateTime.Parse (sDate);
+                                       }
+                                       else if (v is OracleDateTime) {
+                                               OracleDateTime odt = (OracleDateTime) v;
+                                               dt = (DateTime) odt.Value;
+                                       }
+                                       else
+                                               throw new NotImplementedException (); // ?
+                                       
+                                       short year = (short) dt.Year;
+                                       byte month = (byte) dt.Month;
+                                       byte day = (byte) dt.Day;
+                                       byte hour = (byte) dt.Hour;
+                                       byte min = (byte) dt.Minute;
+                                       byte sec = (byte) dt.Second;
+                                       uint fsec = (uint) dt.Millisecond;
+                                       string timezone = "";
+                                       dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
+                                       if (dateTimeDesc == null) {
+                                               OciErrorInfo info = connection.ErrorHandle.HandleError ();
+                                               throw new OracleException (info.ErrorCode, info.ErrorMessage);
+                                       }
+                                       dateTimeDesc.ErrorHandle = connection.ErrorHandle;
+                                       dateTimeDesc.SetDateTime (connection.Session,
+                                               connection.ErrorHandle, 
+                                               year, month, day, hour, min, sec, fsec,
+                                               timezone);
+                                       useRef = true;
+                               }
+                               else if (oracleType == OracleType.DateTime) {
+                                       sDate = "";
+                                       dt = DateTime.MinValue;
+                                       if (v is String) {
+                                               sDate = (string) v;
+                                               dt = DateTime.Parse (sDate);
+                                       }
+                                       else if (v is DateTime)
+                                               dt = (DateTime) v;
+                                       else if (v is OracleString) {
+                                               sDate = (string) v;
+                                               dt = DateTime.Parse (sDate);
+                                       }
+                                       else if (v is OracleDateTime) {
+                                               OracleDateTime odt = (OracleDateTime) v;
+                                               dt = (DateTime) odt.Value;
+                                       }
+                                       else
+                                               throw new NotImplementedException (); // ?
+
+                                       bytes = PackDate (dt);
+                                       bindType = OciDataType.Date;
+                                       bindSize = bytes.Length;
+                               }
+                               else if (oracleType == OracleType.Blob) {
+                                       bytes = (byte[]) v;
+                                       bindType = OciDataType.LongRaw;
+                                       bindSize = bytes.Length;
+                               }
+                               else if (oracleType == OracleType.Clob) {
+                                       string sv = v.ToString();
+                                       rsize = 0;
+                       
+                                       // Get size of buffer
+                                       OciCalls.OCIUnicodeToCharSet (statement.Parent, null, sv, out rsize);
+                       
+                                       // Fill buffer
+                                       bytes = new byte[rsize];
+                                       OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, sv, out rsize);
+
+                                       bindType = OciDataType.Long;
+                                       bindSize = bytes.Length;
+                               }
+                               else if (oracleType == OracleType.Raw) {
+                                       byte[] val = v as byte[];
+                                       bindValue = Marshal.AllocHGlobal (val.Length);
+                                       Marshal.Copy (val, 0, bindValue, val.Length);
+                                       bindSize = val.Length;
+                               }
+                                else if (oracleType == OracleType.Number
+                                        || oracleType == OracleType.Double
+                                        || oracleType == OracleType.Int32
+                                        || oracleType == OracleType.Int16
+                                        || oracleType == OracleType.Byte
+                                        || oracleType == OracleType.Float) {
+                                        string svalue = null;
+                                        if(v is IFormattable)
+                                                svalue = ((IFormattable)v).ToString (null, con.SessionFormatProvider);
+                                        else
+                                                svalue = v.ToString();
+                                       rsize = 0;
+                       
+                                       // Get size of buffer
+                                       OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
+                       
+                                       // Fill buffer
+                                       bytes = new byte[rsize];
+                                       OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
+
+                                       bindType = OciDataType.VarChar2;
+                                        bindSize = svalue.Length;
+                               }
+                               else {
+                                       string svalue = v.ToString () + '\0';
+                                       rsize = 0;
+                       
+                                       // Get size of buffer
+                                       OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
+                       
+                                       // Fill buffer
+                                       bytes = new byte[rsize];
+                                       OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
+
+                                       bindType = OciDataType.String;
+                                        bindSize = svalue.Length;
+                               }
+                       }
+
+                       // Now, call the appropriate OCI Bind function
+
+                       if (useRef == true) {
+                               if (bindType == OciDataType.TimeStamp) {
+                                       bindValue = dateTimeDesc.Handle;
+                                       status = OciCalls.OCIBindByNameRef (statement,
+                                               out tmpHandle,
+                                               connection.ErrorHandle,
+                                               ParameterName,
+                                               ParameterName.Length,
+                                               ref bindValue,
+                                               bindSize,
+                                               bindType,
+                                               ref indicator,
+                                               IntPtr.Zero,
+                                               IntPtr.Zero,
+                                               0,
+                                               IntPtr.Zero,
+                                               0);
+                               }
+                               else {
+                                       status = OciCalls.OCIBindByNameRef (statement,
+                                               out tmpHandle,
+                                               connection.ErrorHandle,
+                                               ParameterName,
+                                               ParameterName.Length,
+                                               ref bindValue,
+                                               bindSize,
+                                               bindType,
+                                               ref indicator,
+                                               IntPtr.Zero,
+                                               IntPtr.Zero,
+                                               0,
+                                               IntPtr.Zero,
+                                               0);
+                               }
+                       }
+                       else if (bindType == OciDataType.RSet) {
+                               status = OciCalls.OCIBindByNameRef (statement,
+                                       out tmpHandle,
+                                       connection.ErrorHandle,
+                                       ParameterName,
+                                       ParameterName.Length,
+                                       ref cursor,
+                                       bindSize,
+                                       bindType,
+                                       ref indicator,
+                                       IntPtr.Zero,
+                                       IntPtr.Zero,
+                                       0,
+                                       IntPtr.Zero,
+                                       0);                                     
+                       }
+                       else if (bytes != null) {
+                               status = OciCalls.OCIBindByNameBytes (statement,
+                                       out tmpHandle,
+                                       connection.ErrorHandle,
+                                       ParameterName,
+                                       ParameterName.Length,
+                                       bytes,
+                                       bindSize,
+                                       bindType,
+                                       ref indicator,
+                                       IntPtr.Zero,
+                                       IntPtr.Zero,
+                                       0,
+                                       IntPtr.Zero,
+                                       0);
+                       }
+                       else {
+                               status = OciCalls.OCIBindByName (statement,
+                                       out tmpHandle,
+                                       connection.ErrorHandle,
+                                       ParameterName,
+                                       ParameterName.Length,
+                                       bindValue,
+                                       bindSize,
+                                       bindType,
+                                       ref indicator,
+                                       IntPtr.Zero,
+                                       IntPtr.Zero,
+                                       0,
+                                       IntPtr.Zero,
+                                       0);
+                       }
+
+                       if (status != 0) {
+                               OciErrorInfo info = connection.ErrorHandle.HandleError ();
+                               throw new OracleException (info.ErrorCode, info.ErrorMessage);
+                       }
+
+                       bindHandle.SetHandle (tmpHandle);
+               }
+
                object ICloneable.Clone ()
                {
-                       throw new NotImplementedException ();
+                       return new OracleParameter(this);
+               }
+
+               private void InferOracleType (object value)
+               {
+                       Type type = value.GetType ();
+                       string exception = String.Format ("The parameter data type of {0} is invalid.", type.Name);
+                       switch (type.FullName) {
+                       case "System.Int64":
+                               SetOracleType (OracleType.Number);
+                               break;
+                       case "System.Boolean":
+                       case "System.Byte":
+                               SetOracleType (OracleType.Byte);
+                               break;
+                       case "System.String":
+                               SetOracleType (OracleType.VarChar);
+                               break;
+                       case "System.DateTime":
+                               SetOracleType (OracleType.DateTime);
+                               break;
+                       case "System.Decimal":
+                               SetOracleType (OracleType.Number);
+                               //scale = ((decimal) value).Scale;
+                               break;
+                       case "System.Double":
+                               SetOracleType (OracleType.Double);
+                               break;
+                       case "System.Byte[]":
+                       case "System.Guid":
+                               SetOracleType (OracleType.Raw);
+                               break;
+                       case "System.Int32":
+                               SetOracleType (OracleType.Int32);
+                               break;
+                       case "System.Single":
+                               SetOracleType (OracleType.Float);
+                               break;
+                       case "System.Int16":
+                               SetOracleType (OracleType.Int16);
+                               break;
+                       default:
+                               throw new ArgumentException (exception);
+                       }
+               }
+
+               private int InferSize ()
+               {
+                       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.TimeStamp:
+                               newSize = 11;
+                               break;          
+                       case OciDataType.Blob:
+                       case OciDataType.Clob:
+                       case OciDataType.RSet: // REF CURSOR
+                               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)
+               {
+                       string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
+                       switch (type) {
+                       case DbType.AnsiString:
+                               oracleType = OracleType.VarChar;
+                               ociType = OciDataType.VarChar;
+                               break;
+                       case DbType.AnsiStringFixedLength:
+                               oracleType = OracleType.Char;
+                               ociType = OciDataType.Char;
+                               break;
+                       case DbType.Binary:
+                       case DbType.Guid:
+                               oracleType = OracleType.Raw;
+                               ociType = OciDataType.Raw;
+                               break;
+                       case DbType.Boolean:
+                       case DbType.Byte:
+                               oracleType = OracleType.Byte;
+                               ociType = OciDataType.Integer;
+                               break;
+                       case DbType.Currency:
+                       case DbType.Decimal:
+                       case DbType.Int64:
+                               oracleType = OracleType.Number;
+                               ociType = OciDataType.Number;
+                               break;
+                       case DbType.Date:
+                       case DbType.DateTime:
+                       case DbType.Time:
+                               oracleType = OracleType.DateTime;
+                               ociType = OciDataType.Char;
+                               break;
+                       case DbType.Double:
+                               oracleType = OracleType.Double;
+                               ociType = OciDataType.Float;
+                               break;
+                       case DbType.Int16:
+                               oracleType = OracleType.Int16;
+                               ociType = OciDataType.Integer;
+                               break;
+                       case DbType.Int32:
+                               oracleType = OracleType.Int32;
+                               ociType = OciDataType.Integer;
+                               break;
+                       case DbType.Object:
+                               oracleType = OracleType.Blob;
+                               ociType = OciDataType.Blob;
+                               break;
+                       case DbType.Single:
+                               oracleType = OracleType.Float;
+                               ociType = OciDataType.Float;
+                               break;
+                       case DbType.String:
+                               oracleType = OracleType.NVarChar;
+                               ociType = OciDataType.VarChar;
+                               break;
+                       case DbType.StringFixedLength:
+                               oracleType = OracleType.NChar;
+                               ociType = OciDataType.Char;
+                               break;
+                       default:
+                               throw new ArgumentException (exception);
+                       }
+                       dbType = type;
+
+               }
+
+               private void SetOracleType (OracleType type)
+               {
+                        FreeHandle ();
+                       string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
+                       switch (type) {
+                       case OracleType.BFile:
+                       case OracleType.Blob:
+                               dbType = DbType.Binary;
+                               ociType = OciDataType.Blob;
+                               break;
+                       case OracleType.LongRaw:
+                       case OracleType.Raw:
+                               dbType = DbType.Binary;
+                               ociType = OciDataType.Raw;
+                               break;
+                       case OracleType.Byte:
+                               dbType = DbType.Byte;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.Char:
+                               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:
+                               dbType = DbType.AnsiString;
+                               ociType = OciDataType.VarChar;
+                               break;
+                       case OracleType.Cursor: // REF CURSOR
+                               ociType = OciDataType.RSet;
+                               dbType = DbType.Object;
+                               break;
+                       case OracleType.IntervalDayToSecond:
+                               dbType = DbType.AnsiStringFixedLength;
+                               ociType = OciDataType.Char;
+                               break;
+                       case OracleType.Timestamp:
+                       case OracleType.TimestampLocal:
+                       case OracleType.TimestampWithTZ:
+                               dbType = DbType.DateTime;
+                               ociType = OciDataType.TimeStamp;
+                               break;
+                       case OracleType.DateTime:
+                               dbType = DbType.DateTime;
+                               ociType = OciDataType.Date;
+                               break;
+                       case OracleType.Double:
+                               dbType = DbType.Double;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.Float:
+                               dbType = DbType.Single;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.Int16:
+                               dbType = DbType.Int16;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.Int32:
+                       case OracleType.IntervalYearToMonth:
+                               dbType = DbType.Int32;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.NChar:
+                               dbType = DbType.StringFixedLength;
+                               ociType = OciDataType.Char;
+                               break;
+                       case OracleType.NClob:
+                       case OracleType.NVarChar:
+                               dbType = DbType.String;
+                               ociType = OciDataType.Char;
+                               break;
+                       case OracleType.Number:
+                               dbType = DbType.VarNumeric;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.SByte:
+                               dbType = DbType.SByte;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.UInt16:
+                               dbType = DbType.UInt16;
+                               ociType = OciDataType.Number;
+                               break;
+                       case OracleType.UInt32:
+                               dbType = DbType.UInt32;
+                               ociType = OciDataType.Number;
+                               break;
+                       default:
+                               throw new ArgumentException (exception);
+                       }
+
+                       oracleType = type;
                }
 
                public override string ToString ()
@@ -157,16 +879,163 @@ namespace System.Data.OracleClient {
                        return ParameterName;
                }
 
-               void SetDbType (DbType dbType)
+               private void GetOutValue (OracleCommand cmd) 
                {
-                       this.dbType = dbType;
+                       // used to update the parameter value
+                       // for Output, the output of InputOutput, and Return parameters
+                       value = DBNull.Value;
+                       if (indicator == -1)
+                               return;
+
+                       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), cmd.Connection.SessionFormatProvider);
+                               break;
+                       case OciDataType.TimeStamp:
+                               value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
+                               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;
+                       case OciDataType.RSet: // REF CURSOR
+                               OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.Environment, cursor);
+                               cursorStatement.ErrorHandle = cmd.ErrorHandle;
+                               cursorStatement.Command = cmd.Connection.CreateCommand ();
+                               cursorStatement.SetupRefCursorResult ();
+                               value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
+                               cursor = IntPtr.Zero;
+                               cursorStatement = null;
+                               break;
+                       default:
+                               throw new NotImplementedException ();
+                       }
+                       tmp = null;
+                       buffer = null;
                }
 
-               void SetOracleType (OracleType oracleType)
+               internal void Update (OracleCommand cmd) 
                {
-                       this.oracleType = oracleType;
+                       if (Direction != ParameterDirection.Input)
+                               GetOutValue (cmd);
+                       
+                       FreeHandle ();
+               }
+
+                internal void FreeHandle ()
+               {
+                       switch (ociType) {
+                       case OciDataType.Clob:
+                       case OciDataType.Blob:
+                               lobLocator = null;
+                               break;
+                       case OciDataType.Raw:
+                               Marshal.FreeHGlobal (bindValue);
+                               break;
+                        case OciDataType.TimeStamp:
+                                break;
+                        default:
+                                Marshal.FreeHGlobal (bindOutValue);
+                                break;
+                       } 
+
+                       bindOutValue = IntPtr.Zero;
+                       bindValue = IntPtr.Zero;
+
+                       bindHandle = null;
+                       errorHandle = null;
+                       connection = null;
+               }
+
+               // 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);
+
+               }
+
+               internal byte[] PackDate (DateTime dateValue) 
+               {
+                       byte[] buffer = new byte[7];
+
+                       buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
+                       buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
+                       buffer[2] = (byte)dateValue.Month;
+                       buffer[3] = (byte)dateValue.Day;
+                       buffer[4] = (byte)(dateValue.Hour+1);
+                       buffer[5] = (byte)(dateValue.Minute+1);
+                       buffer[6] = (byte)(dateValue.Second+1);
+
+                       return buffer;
                }
 
                #endregion // Methods
+
+               internal sealed class OracleParameterConverter : ExpandableObjectConverter
+               {
+                       public OracleParameterConverter ()
+                       {
+                       }
+
+                       [MonoTODO]
+                       public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
+                       {
+                               throw new NotImplementedException ();
+                       }
+
+                       [MonoTODO]
+                       public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+                       {
+                               throw new NotImplementedException ();
+                       }
+               }
        }
 }
+