|
//------------------------------------------------------------------------------
// <copyright file="OdbcParameter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
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
//
// the code causes failure if it is NOT there (remark added by Microsoft)
// it causes failure with jet if it is there
//
// MDAC 76227: Code is required for japanese client/server tests.
// If this causes regressions with Jet please doc here including bug#. (Microsoft)
//
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
// Upgrade input value type if the size of input value is bigger than the max size of the input value type.
switch(_bindtype._sql_type) {
case ODBC32.SQL_TYPE.VARBINARY:
// Max length of VARBINARY is 8,000 of byte array.
if (size > 8000) {
_bindtype = TypeMap._Image; // will change to LONGVARBINARY
}
break;
case ODBC32.SQL_TYPE.VARCHAR:
// Max length of VARCHAR is 8,000 of non-unicode characters.
if (size > 8000) {
_bindtype = TypeMap._Text; // will change to LONGVARCHAR
}
break;
case ODBC32.SQL_TYPE.WVARCHAR:
// Max length of WVARCHAR (NVARCHAR) is 4,000 of unicode characters.
if (size > 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);
}
}
}
}
|