// // System.Data.Odbc.OdbcParameter // // Authors: // Brian Ritchie (brianlritchie@hotmail.com) // Sureshkumar T 2004. // // Copyright (C) Brian Ritchie, 2002 // // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Text; using System.Data; using System.Data.Common; using System.Runtime.InteropServices; using System.Globalization; using System.ComponentModel; namespace System.Data.Odbc { [TypeConverterAttribute (typeof (OdbcParameterConverter))] #if NET_2_0 public sealed class OdbcParameter : DbParameter, ICloneable #else public sealed class OdbcParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable #endif // NET_2_0 { #region Fields string name; ParameterDirection direction; bool isNullable; int size; DataRowVersion sourceVersion; string sourceColumn; byte _precision; byte _scale; object _value; private OdbcTypeMap _typeMap; private NativeBuffer _nativeBuffer = new NativeBuffer (); private NativeBuffer _cbLengthInd; private OdbcParameterCollection container = null; #endregion #region Constructors public OdbcParameter () { _cbLengthInd = new NativeBuffer (); ParameterName = String.Empty; IsNullable = true; SourceColumn = String.Empty; Direction = ParameterDirection.Input; _typeMap = OdbcTypeConverter.GetTypeMap (OdbcType.VarChar); } public OdbcParameter (string name, object value) : this () { this.ParameterName = name; Value = value; _typeMap = OdbcTypeConverter.InferFromValue (value); if (value != null && !value.GetType ().IsValueType) { Type type = value.GetType (); if (type.IsArray) Size = type.GetElementType () == typeof (byte) ? ((Array) value).Length : 0; else Size = value.ToString ().Length; } } public OdbcParameter (string name, OdbcType odbcType) : this () { this.ParameterName = name; _typeMap = (OdbcTypeMap) OdbcTypeConverter.GetTypeMap (odbcType); } public OdbcParameter (string name, OdbcType odbcType, int size) : this (name, odbcType) { this.Size = size; } public OdbcParameter (string name, OdbcType odbcType, int size, string srcColumn) : this (name, odbcType, size) { this.SourceColumn = srcColumn; } [EditorBrowsable (EditorBrowsableState.Advanced)] public OdbcParameter(string name, OdbcType odbcType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value) : this (name, odbcType, size, srcColumn) { this.Direction = direction; this.IsNullable = isNullable; this.SourceVersion = srcVersion; } #endregion #region Properties // Used to ensure that only one collection can contain this // parameter internal OdbcParameterCollection Container { get { return container; } set { container = value; } } [BrowsableAttribute (false)] [OdbcDescriptionAttribute ("The parameter generic type")] [RefreshPropertiesAttribute (RefreshProperties.All)] [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)] [OdbcCategory ("Data")] public #if NET_2_0 override #endif DbType DbType { get { return _typeMap.DbType; } set { if (value == _typeMap.DbType) return; _typeMap = OdbcTypeConverter.GetTypeMap (value); } } [OdbcCategory ("Data")] [OdbcDescriptionAttribute ("Input, output, or bidirectional parameter")] [DefaultValue (ParameterDirection.Input)] public #if NET_2_0 override #endif ParameterDirection Direction { get { return direction; } set { direction = value; } } [BrowsableAttribute (false)] [OdbcDescriptionAttribute ("A design-time property used for strongly typed code generation")] [DesignOnlyAttribute (true)] [EditorBrowsableAttribute (EditorBrowsableState.Advanced)] [DefaultValue (false)] public #if NET_2_0 override #endif bool IsNullable { get { return isNullable; } set { isNullable = value; } } [DefaultValue (OdbcType.NChar)] [OdbcDescriptionAttribute ("The parameter native type")] [RefreshPropertiesAttribute (RefreshProperties.All)] [OdbcCategory ("Data")] public OdbcType OdbcType { get { return _typeMap.OdbcType; } set { if (value == OdbcType) return; _typeMap = OdbcTypeConverter.GetTypeMap (value); } } [OdbcDescription ("DataParameter_ParameterName")] [DefaultValue ("")] public #if NET_2_0 override #endif string ParameterName { get { return name; } set { name = value; } } [OdbcDescription ("DbDataParameter_Precision")] [OdbcCategory ("DataCategory_Data")] [DefaultValue (0)] public byte Precision { get { return _precision; } set { _precision = value; } } [OdbcDescription ("DbDataParameter_Scale")] [OdbcCategory ("DataCategory_Data")] [DefaultValue (0)] public byte Scale { get { return _scale; } set { _scale = value; } } [OdbcDescription ("DbDataParameter_Size")] [OdbcCategory ("DataCategory_Data")] [DefaultValue (0)] public #if NET_2_0 override #endif int Size { get { return size; } set { size = value; } } [OdbcDescription ("DataParameter_SourceColumn")] [OdbcCategory ("DataCategory_Data")] [DefaultValue ("")] public #if NET_2_0 override #endif string SourceColumn { get { return sourceColumn; } set { sourceColumn = value; } } [OdbcDescription ("DataParameter_SourceVersion")] [OdbcCategory ("DataCategory_Data")] [DefaultValue ("Current")] public #if NET_2_0 override #endif DataRowVersion SourceVersion { get { return sourceVersion; } set { sourceVersion = value; } } [TypeConverter (typeof(StringConverter))] [OdbcDescription ("DataParameter_Value")] [OdbcCategory ("DataCategory_Data")] [DefaultValue (null)] public #if NET_2_0 override #endif object Value { get { return _value; } set { _value = value; } } #endregion // Properties #region Methods internal void Bind(IntPtr hstmt, int ParamNum) { OdbcReturn ret; // Convert System.Data.ParameterDirection into odbc enum OdbcInputOutputDirection paramdir = libodbc.ConvertParameterDirection(this.Direction); _cbLengthInd.EnsureAlloc (Marshal.SizeOf (typeof (int))); Marshal.WriteInt32 (_cbLengthInd, GetNativeSize ()); AllocateBuffer (); ret = libodbc.SQLBindParameter(hstmt, (ushort) ParamNum, (short) paramdir, _typeMap.NativeType, _typeMap.SqlType, Convert.ToUInt32(Size), 0, (IntPtr) _nativeBuffer, 0, _cbLengthInd); // Check for error condition if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo)) throw new OdbcException(new OdbcError("SQLBindParam", OdbcHandleType.Stmt, hstmt)); } [MonoTODO] object ICloneable.Clone () { throw new NotImplementedException (); } public override string ToString () { return ParameterName; } private int GetNativeSize () { TextInfo ti = CultureInfo.InvariantCulture.TextInfo; Encoding enc = Encoding.GetEncoding (ti.ANSICodePage); switch (_typeMap.OdbcType) { case OdbcType.Binary: if (Value.GetType ().IsArray && Value.GetType ().GetElementType () == typeof (byte)) return ( (Array) Value).Length; else return Value.ToString ().Length; case OdbcType.Bit: return Marshal.SizeOf (typeof (byte)); case OdbcType.Double: return Marshal.SizeOf (typeof (double)); case OdbcType.Real: return Marshal.SizeOf (typeof (float)); case OdbcType.Int: return Marshal.SizeOf (typeof (int)); case OdbcType.BigInt: return Marshal.SizeOf (typeof (long)); case OdbcType.Decimal: case OdbcType.Numeric: return 19; case OdbcType.SmallInt: return Marshal.SizeOf (typeof (Int16)); case OdbcType.TinyInt: return Marshal.SizeOf (typeof (byte)); case OdbcType.Char: case OdbcType.Text: case OdbcType.VarChar: return enc.GetByteCount (Convert.ToString (Value)) + 1; case OdbcType.NChar: case OdbcType.NText: case OdbcType.NVarChar: // FIXME: Change to unicode return enc.GetByteCount (Convert.ToString (Value)) + 1; case OdbcType.VarBinary: case OdbcType.Image: if (Value.GetType ().IsArray && Value.GetType ().GetElementType () == typeof (byte)) return ( (Array) Value).Length; throw new ArgumentException ("Unsupported Native Type!"); case OdbcType.Date: case OdbcType.DateTime: case OdbcType.SmallDateTime: case OdbcType.Time: case OdbcType.Timestamp: return 18; case OdbcType.UniqueIdentifier: return Marshal.SizeOf (typeof (Guid)); } if (Value.GetType ().IsArray && Value.GetType ().GetElementType () == typeof (byte)) return ( (Array) Value).Length; return Value.ToString ().Length; } private void AllocateBuffer () { int size = GetNativeSize (); if (_nativeBuffer.Size == size) return; _nativeBuffer.AllocBuffer (size); } internal void CopyValue () { if (_nativeBuffer.Handle == IntPtr.Zero) return; DateTime dt; TextInfo ti = CultureInfo.InvariantCulture.TextInfo; Encoding enc = Encoding.GetEncoding (ti.ANSICodePage); byte [] nativeBytes, buffer; switch (_typeMap.OdbcType) { case OdbcType.Binary: throw new NotImplementedException (); case OdbcType.Bit: Marshal.WriteByte (_nativeBuffer, Convert.ToByte (Value)); return; case OdbcType.Double: Marshal.StructureToPtr (Convert.ToDouble (Value), _nativeBuffer, false); return; case OdbcType.Real: Marshal.StructureToPtr (Convert.ToSingle (Value), _nativeBuffer, false); return; case OdbcType.Int: Marshal.WriteInt32 (_nativeBuffer, Convert.ToInt32 (Value)); return; case OdbcType.BigInt: Marshal.WriteInt64 (_nativeBuffer, Convert.ToInt64 (Value)); return; case OdbcType.Decimal: case OdbcType.Numeric: // for numeric, the buffer is a packed decimal struct. // ref http://www.it-faq.pl/mskb/181/254.HTM int [] bits = Decimal.GetBits (Convert.ToDecimal (Value)); buffer = new byte [19]; // ref sqltypes.h buffer [0] = Precision; buffer [1] = (byte) ((bits [3] & 0x00FF0000) >> 16); // scale buffer [2] = (byte) ((bits [3] & 0x80000000) > 0 ? 2 : 1); //sign Buffer.BlockCopy (bits, 0, buffer, 3, 12); // copy data for (int j = 16; j < 19; j++) // pad with 0 buffer [j] = 0; Marshal.Copy (buffer, 0, _nativeBuffer, 19); return; case OdbcType.SmallInt: Marshal.WriteInt16 (_nativeBuffer, Convert.ToInt16 (Value)); return; case OdbcType.TinyInt: Marshal.WriteByte (_nativeBuffer, Convert.ToByte (Value)); return; case OdbcType.Char: case OdbcType.Text: case OdbcType.VarChar: buffer = new byte [GetNativeSize ()]; nativeBytes = enc.GetBytes (Convert.ToString (Value)); Array.Copy (nativeBytes, 0, buffer, 0, nativeBytes.Length); buffer [buffer.Length-1] = (byte) 0; Marshal.Copy (buffer, 0, _nativeBuffer, buffer.Length); Marshal.WriteInt32 (_cbLengthInd, -3); return; case OdbcType.NChar: case OdbcType.NText: case OdbcType.NVarChar: // FIXME : change to unicode buffer = new byte [GetNativeSize ()]; nativeBytes = enc.GetBytes (Convert.ToString (Value)); Array.Copy (nativeBytes, 0, buffer, 0, nativeBytes.Length); buffer [buffer.Length-1] = (byte) 0; Marshal.Copy (buffer, 0, _nativeBuffer, buffer.Length); Marshal.WriteInt32 (_cbLengthInd, -3); return; case OdbcType.VarBinary: case OdbcType.Image: if (Value.GetType ().IsArray && Value.GetType ().GetElementType () == typeof (byte)) { Marshal.Copy ( (byte []) Value, 0, _nativeBuffer, ((byte []) Value).Length); }else throw new ArgumentException ("Unsupported Native Type!"); return; case OdbcType.Date: dt = (DateTime) Value; Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Year); Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Month); Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Day); return; case OdbcType.Time: dt = (DateTime) Value; Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Hour); Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Minute); Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Second); return; case OdbcType.SmallDateTime: case OdbcType.Timestamp: case OdbcType.DateTime: dt = (DateTime) Value; Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Year); Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Month); Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Day); Marshal.WriteInt16 (_nativeBuffer, 6, (short) dt.Hour); Marshal.WriteInt16 (_nativeBuffer, 8, (short) dt.Minute); Marshal.WriteInt16 (_nativeBuffer, 10, (short) dt.Second); Marshal.WriteInt32 (_nativeBuffer, 12, (int) (dt.Ticks % 10000000) * 100); return; case OdbcType.UniqueIdentifier: throw new NotImplementedException (); } if (Value.GetType ().IsArray && Value.GetType ().GetElementType () == typeof (byte)) { Marshal.Copy ( (byte []) Value, 0, _nativeBuffer, ((byte []) Value).Length); }else throw new ArgumentException ("Unsupported Native Type!"); } #if NET_2_0 public override bool SourceColumnNullMapping { get {return false;} set {} } public override void ResetDbType () { throw new NotImplementedException (); } #endif #endregion } }