2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="OdbcParameter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
using System ;
using System.ComponentModel ;
using System.Data ;
using System.Data.Common ;
using System.Data.ProviderBase ;
using System.Data.SqlTypes ;
using System.Diagnostics ;
using System.Globalization ;
using System.Runtime.InteropServices ;
using System.Text ;
using System.Threading ;
namespace System.Data.Odbc {
[
TypeConverterAttribute ( typeof ( System . Data . Odbc . OdbcParameter . OdbcParameterConverter ) )
]
public sealed partial class OdbcParameter : DbParameter , ICloneable , IDbDataParameter {
private bool _hasChanged ;
private bool _userSpecifiedType ;
// _typemap User explicit set type or default parameter type
// _infertpe _typemap if the user explicitly sets type
// otherwise it is infered from the value
// _bindtype The actual type used for binding. E.g. string substitutes numeric
//
// set_DbType: _bindtype = _infertype = _typemap = TypeMap.FromDbType(value)
// set_OdbcType: _bindtype = _infertype = _typemap = TypeMap.FromOdbcType(value)
//
// GetParameterType: If _typemap != _infertype AND value != 0
// _bindtype = _infertype = TypeMap.FromSystemType(value.GetType());
// otherwise
// _bindtype = _infertype
//
// Bind: Bind may change _bindtype if the type is not supported through the driver
//
private TypeMap _typemap ;
private TypeMap _bindtype ;
private string _parameterName ;
private byte _precision ;
private byte _scale ;
private bool _hasScale ;
private ODBC32 . SQL_C _boundSqlCType ;
private ODBC32 . SQL_TYPE _boundParameterType ; // if we bound already that is the type we used
private int _boundSize ;
private int _boundScale ;
private IntPtr _boundBuffer ;
private IntPtr _boundIntbuffer ;
private TypeMap _originalbindtype ; // the original type in case we had to change the bindtype
// (e.g. decimal to string)
private byte _internalPrecision ;
private bool _internalShouldSerializeSize ;
private int _internalSize ;
private ParameterDirection _internalDirection ;
private byte _internalScale ;
private int _internalOffset ;
internal bool _internalUserSpecifiedType ;
private object _internalValue ;
private int _preparedOffset ;
private int _preparedSize ;
private int _preparedBufferSize ;
private object _preparedValue ;
private int _preparedIntOffset ;
private int _preparedValueOffset ;
private ODBC32 . SQL_C _prepared_Sql_C_Type ;
public OdbcParameter ( ) : base ( ) {
// uses System.Threading!
}
public OdbcParameter ( string name , object value ) : this ( ) {
ParameterName = name ;
Value = value ;
}
public OdbcParameter ( string name , OdbcType type ) : this ( ) {
ParameterName = name ;
OdbcType = type ;
}
public OdbcParameter ( string name , OdbcType type , int size ) : this ( ) {
ParameterName = name ;
OdbcType = type ;
Size = size ;
}
public OdbcParameter ( string name , OdbcType type , int size , string sourcecolumn ) : this ( ) {
ParameterName = name ;
OdbcType = type ;
Size = size ;
SourceColumn = sourcecolumn ;
}
[ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
public OdbcParameter ( string parameterName ,
OdbcType odbcType ,
int size ,
ParameterDirection parameterDirection ,
Boolean isNullable ,
Byte precision ,
Byte scale ,
string srcColumn ,
DataRowVersion srcVersion ,
object value
) : this ( ) { // V1.0 everything
this . ParameterName = parameterName ;
this . OdbcType = odbcType ;
this . Size = size ;
this . Direction = parameterDirection ;
this . IsNullable = isNullable ;
PrecisionInternal = precision ;
ScaleInternal = scale ;
this . SourceColumn = srcColumn ;
this . SourceVersion = srcVersion ;
this . Value = value ;
}
[ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
public OdbcParameter ( string parameterName ,
OdbcType odbcType , int size ,
ParameterDirection parameterDirection ,
Byte precision , Byte scale ,
string sourceColumn , DataRowVersion sourceVersion , bool sourceColumnNullMapping ,
object value ) : this ( ) { // V2.0 everything - round trip all browsable properties + precision/scale
this . ParameterName = parameterName ;
this . OdbcType = odbcType ;
this . Size = size ;
this . Direction = parameterDirection ;
this . PrecisionInternal = precision ;
this . ScaleInternal = scale ;
this . SourceColumn = sourceColumn ;
this . SourceVersion = sourceVersion ;
this . SourceColumnNullMapping = sourceColumnNullMapping ;
this . Value = value ;
}
override public System . Data . DbType DbType {
get {
if ( _userSpecifiedType ) {
return _typemap . _dbType ;
}
return TypeMap . _NVarChar . _dbType ; // default type
}
set {
if ( ( null = = _typemap ) | | ( _typemap . _dbType ! = value ) ) {
PropertyTypeChanging ( ) ;
_typemap = TypeMap . FromDbType ( value ) ;
_userSpecifiedType = true ;
}
}
}
public override void ResetDbType ( ) {
ResetOdbcType ( ) ;
}
[
DefaultValue ( OdbcType . NChar ) ,
RefreshProperties ( RefreshProperties . All ) ,
ResCategoryAttribute ( Res . DataCategory_Data ) ,
ResDescriptionAttribute ( Res . OdbcParameter_OdbcType ) ,
System . Data . Common . DbProviderSpecificTypePropertyAttribute ( true ) ,
]
public OdbcType OdbcType {
get {
if ( _userSpecifiedType ) {
return _typemap . _odbcType ;
}
return TypeMap . _NVarChar . _odbcType ; // default type
}
set {
if ( ( null = = _typemap ) | | ( _typemap . _odbcType ! = value ) ) {
PropertyTypeChanging ( ) ;
_typemap = TypeMap . FromOdbcType ( value ) ;
_userSpecifiedType = true ;
}
}
}
public void ResetOdbcType ( ) {
PropertyTypeChanging ( ) ;
_typemap = null ;
_userSpecifiedType = false ;
}
internal bool HasChanged {
set {
_hasChanged = value ;
}
}
internal bool UserSpecifiedType {
get {
return _userSpecifiedType ;
}
}
[
ResCategoryAttribute ( Res . DataCategory_Data ) ,
ResDescriptionAttribute ( Res . DbParameter_ParameterName ) ,
]
override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300
get {
string parameterName = _parameterName ;
return ( ( null ! = parameterName ) ? parameterName : ADP . StrEmpty ) ;
}
set {
if ( _parameterName ! = value ) {
PropertyChanging ( ) ;
_parameterName = value ;
}
}
}
[DefaultValue((Byte)0)] // MDAC 65862
[ResCategoryAttribute(Res.DataCategory_Data)]
[ResDescriptionAttribute(Res.DbDataParameter_Precision)]
public new Byte Precision {
get {
return PrecisionInternal ;
}
set {
PrecisionInternal = value ;
}
}
internal byte PrecisionInternal {
get {
byte precision = _precision ;
if ( 0 = = precision ) {
precision = ValuePrecision ( Value ) ;
}
return precision ;
}
set {
if ( _precision ! = value ) {
PropertyChanging ( ) ;
_precision = value ;
}
}
}
private bool ShouldSerializePrecision ( ) {
return ( 0 ! = _precision ) ;
}
[DefaultValue((Byte)0)] // MDAC 65862
[ResCategoryAttribute(Res.DataCategory_Data)]
[ResDescriptionAttribute(Res.DbDataParameter_Scale)]
public new Byte Scale {
get {
return ScaleInternal ;
}
set {
ScaleInternal = value ;
}
}
internal byte ScaleInternal {
get {
byte scale = _scale ;
if ( ! ShouldSerializeScale ( scale ) ) { // WebData 94688
scale = ValueScale ( Value ) ;
}
return scale ;
}
set {
if ( _scale ! = value | | ! _hasScale ) {
PropertyChanging ( ) ;
_scale = value ;
_hasScale = true ;
}
}
}
private bool ShouldSerializeScale ( ) {
return ShouldSerializeScale ( _scale ) ;
}
private bool ShouldSerializeScale ( byte scale ) {
return _hasScale & & ( ( 0 ! = scale ) | | ShouldSerializePrecision ( ) ) ;
}
// returns the count of bytes for the data (ColumnSize argument to SqlBindParameter)
private int GetColumnSize ( object value , int offset , int ordinal ) {
if ( ( ODBC32 . SQL_C . NUMERIC = = _bindtype . _sql_c ) & & ( 0 ! = _internalPrecision ) ) {
return Math . Min ( ( int ) _internalPrecision , ADP . DecimalMaxPrecision ) ;
}
int cch = _bindtype . _columnSize ;
if ( 0 > = cch ) {
if ( ODBC32 . SQL_C . NUMERIC = = _typemap . _sql_c ) {
cch = 62 ; // (DecimalMaxPrecision+sign+terminator)*BytesPerUnicodeCharater
}
else {
cch = _internalSize ;
if ( ! _internalShouldSerializeSize | | 0x3fffffff < = cch | | cch < 0 ) {
Debug . Assert ( ( ODBC32 . SQL_C . WCHAR = = _bindtype . _sql_c ) | | ( ODBC32 . SQL_C . BINARY = = _bindtype . _sql_c ) , "not wchar or binary" ) ;
if ( ! _internalShouldSerializeSize & & ( 0 ! = ( ParameterDirection . Output & _internalDirection ) ) ) {
throw ADP . UninitializedParameterSize ( ordinal , _bindtype . _type ) ;
}
if ( ( null = = value ) | | Convert . IsDBNull ( value ) ) {
cch = 0 ;
}
else if ( value is String ) {
cch = ( ( String ) value ) . Length - offset ;
if ( ( 0 ! = ( ParameterDirection . Output & _internalDirection ) ) & & ( 0x3fffffff < = _internalSize ) ) {
// restrict output parameters when user set Size to Int32.MaxValue
// to the greater of intput size or 8K
cch = Math . Max ( cch , 4 * 1024 ) ; // MDAC 69224
}
// the following code causes failure against SQL 6.5
// ERROR [HY104] [Microsoft][ODBC SQL Server Driver]Invalid precision value
//
2017-08-21 15:34:15 +00:00
// the code causes failure if it is NOT there (remark added by Microsoft)
2016-08-03 10:59:49 +00:00
// it causes failure with jet if it is there
//
// MDAC 76227: Code is required for japanese client/server tests.
2017-08-21 15:34:15 +00:00
// If this causes regressions with Jet please doc here including bug#. (Microsoft)
2016-08-03 10:59:49 +00:00
//
if ( ( ODBC32 . SQL_TYPE . CHAR = = _bindtype . _sql_type )
| | ( ODBC32 . SQL_TYPE . VARCHAR = = _bindtype . _sql_type )
| | ( ODBC32 . SQL_TYPE . LONGVARCHAR = = _bindtype . _sql_type ) ) {
cch = System . Text . Encoding . Default . GetMaxByteCount ( cch ) ;
}
}
else if ( value is char [ ] ) {
cch = ( ( char [ ] ) value ) . Length - offset ;
if ( ( 0 ! = ( ParameterDirection . Output & _internalDirection ) ) & & ( 0x3fffffff < = _internalSize ) ) {
cch = Math . Max ( cch , 4 * 1024 ) ; // MDAC 69224
}
if ( ( ODBC32 . SQL_TYPE . CHAR = = _bindtype . _sql_type )
| | ( ODBC32 . SQL_TYPE . VARCHAR = = _bindtype . _sql_type )
| | ( ODBC32 . SQL_TYPE . LONGVARCHAR = = _bindtype . _sql_type ) ) {
cch = System . Text . Encoding . Default . GetMaxByteCount ( cch ) ;
}
}
else if ( value is byte [ ] ) {
cch = ( ( byte [ ] ) value ) . Length - offset ;
if ( ( 0 ! = ( ParameterDirection . Output & _internalDirection ) ) & & ( 0x3fffffff < = _internalSize ) ) {
// restrict output parameters when user set Size to Int32.MaxValue
// to the greater of intput size or 8K
cch = Math . Max ( cch , 8 * 1024 ) ; // MDAC 69224
}
}
#if DEBUG
else { Debug . Assert ( false , "not expecting this" ) ; }
#endif
// Note: ColumnSize should never be 0,
// this represents the size of the column on the backend.
//
// without the following code causes failure
//ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value
cch = Math . Max ( 2 , cch ) ;
}
}
}
Debug . Assert ( ( 0 < = cch ) & & ( cch < 0x3fffffff ) , String . Format ( ( IFormatProvider ) null , "GetColumnSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}" , cch , _internalShouldSerializeSize , _internalSize ) ) ;
return cch ;
}
// Return the count of bytes for the data (size in bytes for the native buffer)
//
private int GetValueSize ( object value , int offset ) {
if ( ( ODBC32 . SQL_C . NUMERIC = = _bindtype . _sql_c ) & & ( 0 ! = _internalPrecision ) ) {
return Math . Min ( ( int ) _internalPrecision , ADP . DecimalMaxPrecision ) ;
}
int cch = _bindtype . _columnSize ;
if ( 0 > = cch ) {
bool twobytesperunit = false ;
if ( value is String ) {
cch = ( ( string ) value ) . Length - offset ;
twobytesperunit = true ;
}
else if ( value is char [ ] ) {
cch = ( ( char [ ] ) value ) . Length - offset ;
twobytesperunit = true ;
}
else if ( value is byte [ ] ) {
cch = ( ( byte [ ] ) value ) . Length - offset ;
}
else {
cch = 0 ;
}
if ( _internalShouldSerializeSize & & ( _internalSize > = 0 ) & & ( _internalSize < cch ) & & ( _bindtype = = _originalbindtype ) ) {
cch = _internalSize ;
}
if ( twobytesperunit ) {
cch * = 2 ;
}
}
Debug . Assert ( ( 0 < = cch ) & & ( cch < 0x3fffffff ) , String . Format ( ( IFormatProvider ) null , "GetValueSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}" , cch , _internalShouldSerializeSize , _internalSize ) ) ;
return cch ;
}
// return the count of bytes for the data, used for SQLBindParameter
//
private int GetParameterSize ( object value , int offset , int ordinal ) {
int ccb = _bindtype . _bufferSize ;
if ( 0 > = ccb ) {
if ( ODBC32 . SQL_C . NUMERIC = = _typemap . _sql_c ) {
ccb = 518 ; // _bindtype would be VarChar ([0-9]?{255} + '-' + '.') * 2
}
else {
ccb = _internalSize ;
if ( ! _internalShouldSerializeSize | | ( 0x3fffffff < = ccb ) | | ( ccb < 0 ) ) {
Debug . Assert ( ( ODBC32 . SQL_C . WCHAR = = _bindtype . _sql_c ) | | ( ODBC32 . SQL_C . BINARY = = _bindtype . _sql_c ) , "not wchar or binary" ) ;
if ( ( ccb < = 0 ) & & ( 0 ! = ( ParameterDirection . Output & _internalDirection ) ) ) {
throw ADP . UninitializedParameterSize ( ordinal , _bindtype . _type ) ;
}
if ( ( null = = value ) | | Convert . IsDBNull ( value ) ) {
if ( _bindtype . _sql_c = = ODBC32 . SQL_C . WCHAR ) {
ccb = 2 ; // allow for null termination
}
else {
ccb = 0 ;
}
}
else if ( value is String ) {
ccb = ( ( ( String ) value ) . Length - offset ) * 2 + 2 ;
}
else if ( value is char [ ] ) {
ccb = ( ( ( char [ ] ) value ) . Length - offset ) * 2 + 2 ;
}
else if ( value is byte [ ] ) {
ccb = ( ( byte [ ] ) value ) . Length - offset ;
}
#if DEBUG
else { Debug . Assert ( false , "not expecting this" ) ; }
#endif
if ( ( 0 ! = ( ParameterDirection . Output & _internalDirection ) ) & & ( 0x3fffffff < = _internalSize ) ) {
// restrict output parameters when user set Size to Int32.MaxValue
// to the greater of intput size or 8K
ccb = Math . Max ( ccb , 8 * 1024 ) ; // MDAC 69224
}
}
else if ( ODBC32 . SQL_C . WCHAR = = _bindtype . _sql_c ) {
if ( ( value is String ) & & ( ccb < ( ( String ) value ) . Length ) & & ( _bindtype = = _originalbindtype ) ) {
// silently truncate ... MDAC 84408 ... do not truncate upgraded values ... MDAC 84706
ccb = ( ( String ) value ) . Length ;
}
ccb = ( ccb * 2 ) + 2 ; // allow for null termination
}
else if ( ( value is byte [ ] ) & & ( ccb < ( ( byte [ ] ) value ) . Length ) & & ( _bindtype = = _originalbindtype ) ) {
// silently truncate ... MDAC 84408 ... do not truncate upgraded values ... MDAC 84706
ccb = ( ( byte [ ] ) value ) . Length ;
}
}
}
Debug . Assert ( ( 0 < = ccb ) & & ( ccb < 0x3fffffff ) , "GetParameterSize: out of range " + ccb ) ;
return ccb ;
}
private byte GetParameterPrecision ( object value ) {
if ( 0 ! = _internalPrecision & & value is decimal ) {
// from qfe 762
if ( _internalPrecision < 29 ) {
// from SqlClient ...
if ( _internalPrecision ! = 0 ) {
// devnote: If the userspecified precision (_internalPrecision) is less than the actual values precision
// we silently adjust the userspecified precision to the values precision.
byte precision = ( ( SqlDecimal ) ( decimal ) value ) . Precision ;
_internalPrecision = Math . Max ( _internalPrecision , precision ) ; // silently adjust the precision
}
return _internalPrecision ;
}
return ADP . DecimalMaxPrecision ;
}
if ( ( null = = value ) | | ( value is Decimal ) | | Convert . IsDBNull ( value ) ) { // MDAC 60882
return ADP . DecimalMaxPrecision28 ;
}
return 0 ;
}
private byte GetParameterScale ( object value ) {
// For any value that is not decimal simply return the Scale
//
if ( ! ( value is decimal ) ) {
return _internalScale ;
}
// Determin the values scale
// If the user specified a lower scale we return the user specified scale,
// otherwise the values scale
//
byte s = ( byte ) ( ( Decimal . GetBits ( ( Decimal ) value ) [ 3 ] & 0x00ff0000 ) > > 0x10 ) ;
if ( ( _internalScale > 0 ) & & ( _internalScale < s ) ) {
return _internalScale ;
}
return s ;
}
//This is required for OdbcCommand.Clone to deep copy the parameters collection
object ICloneable . Clone ( ) {
return new OdbcParameter ( this ) ;
}
private void CopyParameterInternal ( ) {
_internalValue = Value ;
// we should coerce the parameter value at this time.
_internalPrecision = ShouldSerializePrecision ( ) ? PrecisionInternal : ValuePrecision ( _internalValue ) ;
_internalShouldSerializeSize = ShouldSerializeSize ( ) ;
_internalSize = _internalShouldSerializeSize ? Size : ValueSize ( _internalValue ) ;
_internalDirection = Direction ;
_internalScale = ShouldSerializeScale ( ) ? ScaleInternal : ValueScale ( _internalValue ) ;
_internalOffset = Offset ;
_internalUserSpecifiedType = UserSpecifiedType ;
}
private void CloneHelper ( OdbcParameter destination ) {
CloneHelperCore ( destination ) ;
destination . _userSpecifiedType = _userSpecifiedType ;
destination . _typemap = _typemap ;
destination . _parameterName = _parameterName ;
destination . _precision = _precision ;
destination . _scale = _scale ;
destination . _hasScale = _hasScale ;
}
internal void ClearBinding ( ) {
if ( ! _userSpecifiedType ) {
_typemap = null ;
}
_bindtype = null ;
}
internal void PrepareForBind ( OdbcCommand command , short ordinal , ref int parameterBufferSize ) {
// make a snapshot of the current properties. Properties may change while we work on them
//
CopyParameterInternal ( ) ;
object value = ProcessAndGetParameterValue ( ) ;
int offset = _internalOffset ;
int size = _internalSize ;
ODBC32 . SQL_C sql_c_type ;
// offset validation based on the values type
//
if ( offset > 0 ) {
if ( value is string ) {
if ( offset > ( ( string ) value ) . Length ) {
throw ADP . OffsetOutOfRangeException ( ) ;
}
}
else if ( value is char [ ] ) {
if ( offset > ( ( char [ ] ) value ) . Length ) {
throw ADP . OffsetOutOfRangeException ( ) ;
}
}
else if ( value is byte [ ] ) {
if ( offset > ( ( byte [ ] ) value ) . Length ) {
throw ADP . OffsetOutOfRangeException ( ) ;
}
}
else {
// for all other types offset has no meaning
// this is important since we might upgrade some types to strings
offset = 0 ;
}
}
// type support verification for certain data types
//
switch ( _bindtype . _sql_type ) {
case ODBC32 . SQL_TYPE . DECIMAL :
case ODBC32 . SQL_TYPE . NUMERIC :
if (
! command . Connection . IsV3Driver // for non V3 driver we always do the conversion
| | ! command . Connection . TestTypeSupport ( ODBC32 . SQL_TYPE . NUMERIC ) // otherwise we convert if the driver does not support numeric
| | command . Connection . TestRestrictedSqlBindType ( _bindtype . _sql_type ) // or the type is not supported
) {
// No support for NUMERIC
// Change the type
_bindtype = TypeMap . _VarChar ;
if ( ( null ! = value ) & & ! Convert . IsDBNull ( value ) ) {
value = ( ( Decimal ) value ) . ToString ( CultureInfo . CurrentCulture ) ;
size = ( ( string ) value ) . Length ;
offset = 0 ;
}
}
break ;
case ODBC32 . SQL_TYPE . BIGINT :
if ( ! command . Connection . IsV3Driver ) {
// No support for BIGINT
// Change the type
_bindtype = TypeMap . _VarChar ;
if ( ( null ! = value ) & & ! Convert . IsDBNull ( value ) ) {
value = ( ( Int64 ) value ) . ToString ( CultureInfo . CurrentCulture ) ;
size = ( ( string ) value ) . Length ;
offset = 0 ;
}
}
break ;
case ODBC32 . SQL_TYPE . WCHAR : // MDAC 68993
case ODBC32 . SQL_TYPE . WVARCHAR :
case ODBC32 . SQL_TYPE . WLONGVARCHAR :
if ( value is Char ) {
value = value . ToString ( ) ;
size = ( ( string ) value ) . Length ;
offset = 0 ;
}
if ( ! command . Connection . TestTypeSupport ( _bindtype . _sql_type ) ) {
// No support for WCHAR, WVARCHAR or WLONGVARCHAR
// Change the type
if ( ODBC32 . SQL_TYPE . WCHAR = = _bindtype . _sql_type ) { _bindtype = TypeMap . _Char ; }
else if ( ODBC32 . SQL_TYPE . WVARCHAR = = _bindtype . _sql_type ) { _bindtype = TypeMap . _VarChar ; }
else if ( ODBC32 . SQL_TYPE . WLONGVARCHAR = = _bindtype . _sql_type ) {
_bindtype = TypeMap . _Text ;
}
}
break ;
} // end switch
// Conversation from WCHAR to CHAR, VARCHAR or LONVARCHAR (AnsiString) is different for some providers
// we need to chonvert WCHAR to CHAR and bind as sql_c_type = CHAR
//
sql_c_type = _bindtype . _sql_c ;
if ( ! command . Connection . IsV3Driver ) {
if ( sql_c_type = = ODBC32 . SQL_C . WCHAR ) {
sql_c_type = ODBC32 . SQL_C . CHAR ;
if ( null ! = value ) {
if ( ! Convert . IsDBNull ( value ) & & value is string ) {
int lcid = System . Globalization . CultureInfo . CurrentCulture . LCID ;
CultureInfo culInfo = new CultureInfo ( lcid ) ;
Encoding cpe = System . Text . Encoding . GetEncoding ( culInfo . TextInfo . ANSICodePage ) ;
value = cpe . GetBytes ( value . ToString ( ) ) ;
size = ( ( byte [ ] ) value ) . Length ;
}
}
}
} ;
int cbParameterSize = GetParameterSize ( value , offset , ordinal ) ; // count of bytes for the data, for SQLBindParameter
// here we upgrade the datatypes if the given values size is bigger than the types columnsize
//
switch ( _bindtype . _sql_type ) {
case ODBC32 . SQL_TYPE . VARBINARY : // MDAC 74372
// Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding
if ( ( cbParameterSize > 8000 ) )
{ _bindtype = TypeMap . _Image ; } // will change to LONGVARBINARY
break ;
case ODBC32 . SQL_TYPE . VARCHAR : // MDAC 74372
// Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding
if ( ( cbParameterSize > 8000 ) )
{ _bindtype = TypeMap . _Text ; } // will change to LONGVARCHAR
break ;
case ODBC32 . SQL_TYPE . WVARCHAR : // MDAC 75099
// Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding
if ( ( cbParameterSize > 4000 ) )
{ _bindtype = TypeMap . _NText ; } // will change to WLONGVARCHAR
break ;
}
_prepared_Sql_C_Type = sql_c_type ;
_preparedOffset = offset ;
_preparedSize = size ;
_preparedValue = value ;
_preparedBufferSize = cbParameterSize ;
_preparedIntOffset = parameterBufferSize ;
_preparedValueOffset = _preparedIntOffset + IntPtr . Size ;
parameterBufferSize + = ( cbParameterSize + IntPtr . Size ) ;
}
internal void Bind ( OdbcStatementHandle hstmt , OdbcCommand command , short ordinal , CNativeBuffer parameterBuffer , bool allowReentrance ) {
ODBC32 . RetCode retcode ;
ODBC32 . SQL_C sql_c_type = _prepared_Sql_C_Type ;
ODBC32 . SQL_PARAM sqldirection = SqlDirectionFromParameterDirection ( ) ;
int offset = _preparedOffset ;
int size = _preparedSize ;
object value = _preparedValue ;
int cbValueSize = GetValueSize ( value , offset ) ; // count of bytes for the data
int cchSize = GetColumnSize ( value , offset , ordinal ) ; // count of bytes for the data, used to allocate the buffer length
byte precision = GetParameterPrecision ( value ) ;
byte scale = GetParameterScale ( value ) ;
int cbActual ;
HandleRef valueBuffer = parameterBuffer . PtrOffset ( _preparedValueOffset , _preparedBufferSize ) ;
HandleRef intBuffer = parameterBuffer . PtrOffset ( _preparedIntOffset , IntPtr . Size ) ;
// for the numeric datatype we need to do some special case handling ...
//
if ( ODBC32 . SQL_C . NUMERIC = = sql_c_type ) {
// for input/output parameters we need to adjust the scale of the input value since the convert function in
// sqlsrv32 takes this scale for the output parameter (possible bug in sqlsrv32?)
//
if ( ( ODBC32 . SQL_PARAM . INPUT_OUTPUT = = sqldirection ) & & ( value is Decimal ) ) {
if ( scale < _internalScale ) {
while ( scale < _internalScale ) {
value = ( ( decimal ) value ) * 10 ;
scale + + ;
}
}
}
SetInputValue ( value , sql_c_type , cbValueSize , precision , 0 , parameterBuffer ) ;
// for output parameters we need to write precision and scale to the buffer since the convert function in
// sqlsrv32 expects these values there (possible bug in sqlsrv32?)
//
if ( ODBC32 . SQL_PARAM . INPUT ! = sqldirection ) {
parameterBuffer . WriteInt16 ( _preparedValueOffset , ( short ) ( ( ( ushort ) scale < < 8 ) | ( ushort ) precision ) ) ;
}
}
else {
SetInputValue ( value , sql_c_type , cbValueSize , size , offset , parameterBuffer ) ;
}
// Try to reuse existing bindings if
// the binding is valid (means we already went through binding all parameters)
// the parametercollection is bound already
// the bindtype ParameterType did not change (forced upgrade)
if ( ! _hasChanged
& & ( _boundSqlCType = = sql_c_type )
& & ( _boundParameterType = = _bindtype . _sql_type )
& & ( _boundSize = = cchSize )
& & ( _boundScale = = scale )
& & ( _boundBuffer = = valueBuffer . Handle )
& & ( _boundIntbuffer = = intBuffer . Handle )
) {
return ;
}
//SQLBindParameter
retcode = hstmt . BindParameter (
ordinal , // Parameter Number
( short ) sqldirection , // InputOutputType
sql_c_type , // ValueType
_bindtype . _sql_type , // ParameterType
( IntPtr ) cchSize , // ColumnSize
( IntPtr ) scale , // DecimalDigits
valueBuffer , // ParameterValuePtr
( IntPtr ) _preparedBufferSize ,
intBuffer ) ; // StrLen_or_IndPtr
if ( ODBC32 . RetCode . SUCCESS ! = retcode ) {
if ( "07006" = = command . GetDiagSqlState ( ) ) {
Bid . Trace ( "<odbc.OdbcParameter.Bind|ERR> Call to BindParameter returned errorcode [07006]\n" ) ;
command . Connection . FlagRestrictedSqlBindType ( _bindtype . _sql_type ) ;
if ( allowReentrance ) {
this . Bind ( hstmt , command , ordinal , parameterBuffer , false ) ;
return ;
}
}
command . Connection . HandleError ( hstmt , retcode ) ;
}
_hasChanged = false ;
_boundSqlCType = sql_c_type ;
_boundParameterType = _bindtype . _sql_type ;
_boundSize = cchSize ;
_boundScale = scale ;
_boundBuffer = valueBuffer . Handle ;
_boundIntbuffer = intBuffer . Handle ;
if ( ODBC32 . SQL_C . NUMERIC = = sql_c_type ) {
OdbcDescriptorHandle hdesc = command . GetDescriptorHandle ( ODBC32 . SQL_ATTR . APP_PARAM_DESC ) ;
// descriptor handle is cached on command wrapper, don't release it
// Set descriptor Type
//
//SQLSetDescField(hdesc, i+1, SQL_DESC_TYPE, (void *)SQL_C_NUMERIC, 0);
retcode = hdesc . SetDescriptionField1 ( ordinal , ODBC32 . SQL_DESC . TYPE , ( IntPtr ) ODBC32 . SQL_C . NUMERIC ) ;
if ( ODBC32 . RetCode . SUCCESS ! = retcode ) {
command . Connection . HandleError ( hstmt , retcode ) ;
}
// Set precision
//
cbActual = ( int ) precision ;
//SQLSetDescField(hdesc, i+1, SQL_DESC_PRECISION, (void *)precision, 0);
retcode = hdesc . SetDescriptionField1 ( ordinal , ODBC32 . SQL_DESC . PRECISION , ( IntPtr ) cbActual ) ;
if ( ODBC32 . RetCode . SUCCESS ! = retcode ) {
command . Connection . HandleError ( hstmt , retcode ) ;
}
// Set scale
//
// SQLSetDescField(hdesc, i+1, SQL_DESC_SCALE, (void *)llen, 0);
cbActual = ( int ) scale ;
retcode = hdesc . SetDescriptionField1 ( ordinal , ODBC32 . SQL_DESC . SCALE , ( IntPtr ) cbActual ) ;
if ( ODBC32 . RetCode . SUCCESS ! = retcode ) {
command . Connection . HandleError ( hstmt , retcode ) ;
}
// Set data pointer
//
// SQLSetDescField(hdesc, i+1, SQL_DESC_DATA_PTR, (void *)&numeric, 0);
retcode = hdesc . SetDescriptionField2 ( ordinal , ODBC32 . SQL_DESC . DATA_PTR , valueBuffer ) ;
if ( ODBC32 . RetCode . SUCCESS ! = retcode ) {
command . Connection . HandleError ( hstmt , retcode ) ;
}
}
}
internal void GetOutputValue ( CNativeBuffer parameterBuffer ) { //Handle any output params
// No value is available if the user fiddles with the parameters properties
//
if ( _hasChanged ) return ;
if ( ( null ! = _bindtype ) & & ( _internalDirection ! = ParameterDirection . Input ) ) {
TypeMap typemap = _bindtype ;
_bindtype = null ;
int cbActual = ( int ) parameterBuffer . ReadIntPtr ( _preparedIntOffset ) ;
if ( ODBC32 . SQL_NULL_DATA = = cbActual ) {
Value = DBNull . Value ;
}
else if ( ( 0 < = cbActual ) | | ( cbActual = = ODBC32 . SQL_NTS ) ) { // safeguard
Value = parameterBuffer . MarshalToManaged ( _preparedValueOffset , _boundSqlCType , cbActual ) ;
if ( _boundSqlCType = = ODBC32 . SQL_C . CHAR ) {
if ( ( null ! = Value ) & & ! Convert . IsDBNull ( Value ) ) {
int lcid = System . Globalization . CultureInfo . CurrentCulture . LCID ;
CultureInfo culInfo = new CultureInfo ( lcid ) ;
Encoding cpe = System . Text . Encoding . GetEncoding ( culInfo . TextInfo . ANSICodePage ) ;
Value = cpe . GetString ( ( Byte [ ] ) Value ) ;
}
}
if ( ( typemap ! = _typemap ) & & ( null ! = Value ) & & ! Convert . IsDBNull ( Value ) & & ( Value . GetType ( ) ! = _typemap . _type ) ) {
Debug . Assert ( ODBC32 . SQL_C . NUMERIC = = _typemap . _sql_c , "unexpected" ) ;
Value = Decimal . Parse ( ( string ) Value , System . Globalization . CultureInfo . CurrentCulture ) ;
}
}
}
}
private object ProcessAndGetParameterValue ( ) {
object value = _internalValue ;
if ( _internalUserSpecifiedType ) {
if ( ( null ! = value ) & & ! Convert . IsDBNull ( value ) ) {
Type valueType = value . GetType ( ) ;
if ( ! valueType . IsArray ) {
if ( valueType ! = _typemap . _type ) {
try {
value = Convert . ChangeType ( value , _typemap . _type , ( System . IFormatProvider ) null ) ;
}
catch ( Exception e ) {
// Don't know which exception to expect from ChangeType so we filter out the serious ones
//
if ( ! ADP . IsCatchableExceptionType ( e ) ) {
throw ;
}
throw ADP . ParameterConversionFailed ( value , _typemap . _type , e ) ; // WebData 75433
}
}
}
else if ( valueType = = typeof ( char [ ] ) ) {
value = new String ( ( char [ ] ) value ) ;
}
}
}
else if ( null = = _typemap ) {
if ( ( null = = value ) | | Convert . IsDBNull ( value ) ) {
_typemap = TypeMap . _NVarChar ; // default type
}
else {
Type type = value . GetType ( ) ;
_typemap = TypeMap . FromSystemType ( type ) ;
}
}
Debug . Assert ( null ! = _typemap , "GetParameterValue: null _typemap" ) ;
_originalbindtype = _bindtype = _typemap ;
return value ;
}
private void PropertyChanging ( ) {
_hasChanged = true ;
}
private void PropertyTypeChanging ( ) {
PropertyChanging ( ) ;
//CoercedValue = null;
}
internal void SetInputValue ( object value , ODBC32 . SQL_C sql_c_type , int cbsize , int sizeorprecision , int offset , CNativeBuffer parameterBuffer ) { //Handle any input params
if ( ( ParameterDirection . Input = = _internalDirection ) | | ( ParameterDirection . InputOutput = = _internalDirection ) ) {
//Note: (lang) "null" means to use the servers default (not DBNull).
//We probably should just not have bound this parameter, period, but that
//would mess up the users question marks, etc...
if ( ( null = = value ) ) {
parameterBuffer . WriteIntPtr ( _preparedIntOffset , ( IntPtr ) ODBC32 . SQL_DEFAULT_PARAM ) ;
}
else if ( Convert . IsDBNull ( value ) ) {
parameterBuffer . WriteIntPtr ( _preparedIntOffset , ( IntPtr ) ODBC32 . SQL_NULL_DATA ) ;
}
else {
switch ( sql_c_type ) {
case ODBC32 . SQL_C . CHAR :
case ODBC32 . SQL_C . WCHAR :
case ODBC32 . SQL_C . BINARY :
//StrLen_or_IndPtr is ignored except for Character or Binary or data.
parameterBuffer . WriteIntPtr ( _preparedIntOffset , ( IntPtr ) cbsize ) ;
break ;
default :
parameterBuffer . WriteIntPtr ( _preparedIntOffset , IntPtr . Zero ) ;
break ;
}
//Place the input param value into the native buffer
parameterBuffer . MarshalToNative ( _preparedValueOffset , value , sql_c_type , sizeorprecision , offset ) ;
}
}
else {
// always set ouput only and return value parameter values to null when executing
_internalValue = null ;
//Always initialize the intbuffer (for output params). Since we need to know
//if/when the parameters are available for output. (ie: when is the buffer valid...)
//if (_sqldirection != ODBC32.SQL_PARAM.INPUT)
parameterBuffer . WriteIntPtr ( _preparedIntOffset , ( IntPtr ) ODBC32 . SQL_NULL_DATA ) ;
}
}
private ODBC32 . SQL_PARAM SqlDirectionFromParameterDirection ( ) {
switch ( _internalDirection ) {
case ParameterDirection . Input :
return ODBC32 . SQL_PARAM . INPUT ;
case ParameterDirection . Output :
case ParameterDirection . ReturnValue :
//ODBC doesn't seem to distinguish between output and return value
//as SQL_PARAM_RETURN_VALUE fails with "Invalid parameter type"
return ODBC32 . SQL_PARAM . OUTPUT ;
case ParameterDirection . InputOutput :
return ODBC32 . SQL_PARAM . INPUT_OUTPUT ;
default :
Debug . Assert ( false , "Unexpected Direction Property on Parameter" ) ;
return ODBC32 . SQL_PARAM . INPUT ;
}
}
[
RefreshProperties ( RefreshProperties . All ) ,
ResCategoryAttribute ( Res . DataCategory_Data ) ,
ResDescriptionAttribute ( Res . DbParameter_Value ) ,
TypeConverterAttribute ( typeof ( StringConverter ) ) ,
]
override public object Value { // V1.2.3300, XXXParameter V1.0.3300
get {
return _value ;
}
set {
_coercedValue = null ;
_value = value ;
}
}
private byte ValuePrecision ( object value ) {
return ValuePrecisionCore ( value ) ;
}
private byte ValueScale ( object value ) {
return ValueScaleCore ( value ) ;
}
private int ValueSize ( object value ) {
return ValueSizeCore ( value ) ;
}
// implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods
sealed internal class OdbcParameterConverter : ExpandableObjectConverter {
// converter classes should have public ctor
public OdbcParameterConverter ( ) {
}
public override bool CanConvertTo ( ITypeDescriptorContext context , Type destinationType ) {
if ( destinationType = = typeof ( System . ComponentModel . Design . Serialization . InstanceDescriptor ) ) {
return true ;
}
return base . CanConvertTo ( context , destinationType ) ;
}
public override object ConvertTo ( ITypeDescriptorContext context , CultureInfo culture , object value , Type destinationType ) {
if ( destinationType = = null ) {
throw ADP . ArgumentNull ( "destinationType" ) ;
}
if ( destinationType = = typeof ( System . ComponentModel . Design . Serialization . InstanceDescriptor ) & & value is OdbcParameter ) {
OdbcParameter p = ( OdbcParameter ) value ;
// MDAC 67321 - reducing parameter generated code
int flags = 0 ; // if part of the collection - the parametername can't be empty
if ( OdbcType . NChar ! = p . OdbcType ) {
flags | = 1 ;
}
if ( p . ShouldSerializeSize ( ) ) {
flags | = 2 ;
}
if ( ! ADP . IsEmpty ( p . SourceColumn ) ) {
flags | = 4 ;
}
if ( null ! = p . Value ) {
flags | = 8 ;
}
if ( ( ParameterDirection . Input ! = p . Direction ) | | p . IsNullable
| | p . ShouldSerializePrecision ( ) | | p . ShouldSerializeScale ( )
| | ( DataRowVersion . Current ! = p . SourceVersion ) ) {
flags | = 16 ; // V1.0 everything
}
if ( p . SourceColumnNullMapping ) {
flags | = 32 ; // v2.0 everything
}
Type [ ] ctorParams ;
object [ ] ctorValues ;
switch ( flags ) {
case 0 : // ParameterName
case 1 : // SqlDbType
ctorParams = new Type [ ] { typeof ( string ) , typeof ( OdbcType ) } ;
ctorValues = new object [ ] { p . ParameterName , p . OdbcType } ;
break ;
case 2 : // Size
case 3 : // Size, SqlDbType
ctorParams = new Type [ ] { typeof ( string ) , typeof ( OdbcType ) , typeof ( int ) } ;
ctorValues = new object [ ] { p . ParameterName , p . OdbcType , p . Size } ;
break ;
case 4 : // SourceColumn
case 5 : // SourceColumn, SqlDbType
case 6 : // SourceColumn, Size
case 7 : // SourceColumn, Size, SqlDbType
ctorParams = new Type [ ] { typeof ( string ) , typeof ( OdbcType ) , typeof ( int ) , typeof ( string ) } ;
ctorValues = new object [ ] { p . ParameterName , p . OdbcType , p . Size , p . SourceColumn } ;
break ;
case 8 : // Value
ctorParams = new Type [ ] { typeof ( string ) , typeof ( object ) } ;
ctorValues = new object [ ] { p . ParameterName , p . Value } ;
break ;
default :
if ( 0 = = ( 32 & flags ) ) { // V1.0 everything
ctorParams = new Type [ ] {
typeof ( string ) , typeof ( OdbcType ) , typeof ( int ) , typeof ( ParameterDirection ) ,
typeof ( bool ) , typeof ( byte ) , typeof ( byte ) , typeof ( string ) ,
typeof ( DataRowVersion ) , typeof ( object ) } ;
ctorValues = new object [ ] {
p . ParameterName , p . OdbcType , p . Size , p . Direction ,
p . IsNullable , p . PrecisionInternal , p . ScaleInternal , p . SourceColumn ,
p . SourceVersion , p . Value } ;
}
else { // v2.0 everything - round trip all browsable properties + precision/scale
ctorParams = new Type [ ] {
typeof ( string ) , typeof ( OdbcType ) , typeof ( int ) , typeof ( ParameterDirection ) ,
typeof ( byte ) , typeof ( byte ) ,
typeof ( string ) , typeof ( DataRowVersion ) , typeof ( bool ) ,
typeof ( object ) } ;
ctorValues = new object [ ] {
p . ParameterName , p . OdbcType , p . Size , p . Direction ,
p . PrecisionInternal , p . ScaleInternal ,
p . SourceColumn , p . SourceVersion , p . SourceColumnNullMapping ,
p . Value } ;
}
break ;
}
System . Reflection . ConstructorInfo ctor = typeof ( OdbcParameter ) . GetConstructor ( ctorParams ) ;
if ( null ! = ctor ) {
return new System . ComponentModel . Design . Serialization . InstanceDescriptor ( ctor , ctorValues ) ;
}
}
return base . ConvertTo ( context , culture , value , destinationType ) ;
}
}
}
}