2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="SqlParameter.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
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System ;
using System.ComponentModel ;
using System.Collections.Generic ;
using System.Data ;
using System.Data.Common ;
using System.Data.ProviderBase ;
using System.Data.Sql ;
using System.Data.SqlTypes ;
using System.Diagnostics ;
using System.IO ;
using System.Globalization ;
using System.Reflection ;
using System.Text ;
using System.Xml ;
using MSS = Microsoft . SqlServer . Server ;
using Microsoft.SqlServer.Server ;
using System.Threading.Tasks ;
internal abstract class DataFeed {
}
internal class StreamDataFeed : DataFeed {
internal Stream _source ;
internal StreamDataFeed ( Stream source ) {
_source = source ;
}
}
internal class TextDataFeed : DataFeed {
internal TextReader _source ;
internal TextDataFeed ( TextReader source ) {
_source = source ;
}
}
internal class XmlDataFeed : DataFeed {
internal XmlReader _source ;
internal XmlDataFeed ( XmlReader source ) {
_source = source ;
}
}
[
System . ComponentModel . TypeConverterAttribute ( typeof ( System . Data . SqlClient . SqlParameter . SqlParameterConverter ) )
]
public sealed partial class SqlParameter : DbParameter , IDbDataParameter , ICloneable {
private MetaType _metaType ;
private SqlCollation _collation ;
private string _xmlSchemaCollectionDatabase ;
private string _xmlSchemaCollectionOwningSchema ;
private string _xmlSchemaCollectionName ;
private string _udtTypeName ;
private string _typeName ;
private Type _udtType ;
private Exception _udtLoadError ;
private string _parameterName ;
private byte _precision ;
private byte _scale ;
private bool _hasScale ; // V1.0 compat, ignore _hasScale
private MetaType _internalMetaType ;
private SqlBuffer _sqlBufferReturnValue ;
private INullable _valueAsINullable ;
private bool _isSqlParameterSqlType ;
private bool _isNull = true ;
private bool _coercedValueIsSqlType ;
private bool _coercedValueIsDataFeed ;
private int _actualSize = - 1 ;
/// <summary>
/// Column Encryption Cipher Related Metadata.
/// </summary>
private SqlCipherMetadata _columnEncryptionCipherMetadata ;
/// <summary>
/// Get or set the encryption related metadata of this SqlParameter.
/// Should be set to a non-null value only once.
/// </summary>
internal SqlCipherMetadata CipherMetadata {
get {
return _columnEncryptionCipherMetadata ;
}
set {
_columnEncryptionCipherMetadata = value ;
}
}
/// <summary>
/// Indicates if the parameter encryption metadata received by sp_describe_parameter_encryption.
/// For unencrypted parameters, the encryption metadata should still be sent (and will indicate
/// that no encryption is needed).
/// </summary>
internal bool HasReceivedMetadata { get ; set ; }
/// <summary>
/// Returns the normalization rule version number as a byte
/// </summary>
internal byte NormalizationRuleVersion {
get {
if ( _columnEncryptionCipherMetadata ! = null ) {
return _columnEncryptionCipherMetadata . NormalizationRuleVersion ;
}
return 0x00 ;
}
}
public SqlParameter ( ) : base ( ) {
}
[ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
public SqlParameter ( string parameterName ,
SqlDbType dbType , int size ,
ParameterDirection direction , bool isNullable ,
byte precision , byte scale ,
string sourceColumn , DataRowVersion sourceVersion ,
object value ) : this ( ) { // V1.0 everything
this . ParameterName = parameterName ;
this . SqlDbType = dbType ;
this . Size = size ;
this . Direction = direction ;
this . IsNullable = isNullable ;
PrecisionInternal = precision ;
ScaleInternal = scale ;
this . SourceColumn = sourceColumn ;
this . SourceVersion = sourceVersion ;
this . Value = value ;
}
public SqlParameter ( string parameterName ,
SqlDbType dbType , int size ,
ParameterDirection direction ,
byte precision , byte scale ,
string sourceColumn , DataRowVersion sourceVersion , bool sourceColumnNullMapping ,
object value ,
string xmlSchemaCollectionDatabase , string xmlSchemaCollectionOwningSchema ,
string xmlSchemaCollectionName
) { // V2.0 everything - round trip all browsable properties + precision/scale
this . ParameterName = parameterName ;
this . SqlDbType = dbType ;
this . Size = size ;
this . Direction = direction ;
this . PrecisionInternal = precision ;
this . ScaleInternal = scale ;
this . SourceColumn = sourceColumn ;
this . SourceVersion = sourceVersion ;
this . SourceColumnNullMapping = sourceColumnNullMapping ;
this . Value = value ;
this . _xmlSchemaCollectionDatabase = xmlSchemaCollectionDatabase ;
this . _xmlSchemaCollectionOwningSchema = xmlSchemaCollectionOwningSchema ;
this . _xmlSchemaCollectionName = xmlSchemaCollectionName ;
}
public SqlParameter ( string parameterName , SqlDbType dbType ) : this ( ) {
this . ParameterName = parameterName ;
this . SqlDbType = dbType ;
}
public SqlParameter ( string parameterName , object value ) : this ( ) {
Debug . Assert ( ! ( value is SqlDbType ) , "use SqlParameter(string, SqlDbType)" ) ;
this . ParameterName = parameterName ;
this . Value = value ;
}
public SqlParameter ( string parameterName , SqlDbType dbType , int size ) : this ( ) {
this . ParameterName = parameterName ;
this . SqlDbType = dbType ;
this . Size = size ;
}
public SqlParameter ( string parameterName , SqlDbType dbType , int size , string sourceColumn ) : this ( ) {
this . ParameterName = parameterName ;
this . SqlDbType = dbType ;
this . Size = size ;
this . SourceColumn = sourceColumn ;
}
//
// currently the user can't set this value. it gets set by the returnvalue from tds
//
internal SqlCollation Collation {
get {
return _collation ;
}
set {
_collation = value ;
}
}
[
Browsable ( false ) ,
]
public SqlCompareOptions CompareInfo {
// Bits 21 through 25 represent the CompareInfo
get {
SqlCollation collation = _collation ;
if ( null ! = collation ) {
return collation . SqlCompareOptions ;
}
return SqlCompareOptions . None ;
}
set {
SqlCollation collation = _collation ;
if ( null = = collation ) {
_collation = collation = new SqlCollation ( ) ;
}
if ( ( value & SqlString . x_iValidSqlCompareOptionMask ) ! = value ) {
throw ADP . ArgumentOutOfRange ( "CompareInfo" ) ;
}
collation . SqlCompareOptions = value ;
}
}
[
ResCategoryAttribute ( Res . DataCategory_Xml ) ,
ResDescriptionAttribute ( Res . SqlParameter_XmlSchemaCollectionDatabase ) ,
]
public string XmlSchemaCollectionDatabase {
get {
string xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase ;
return ( ( xmlSchemaCollectionDatabase ! = null ) ? xmlSchemaCollectionDatabase : ADP . StrEmpty ) ;
}
set {
_xmlSchemaCollectionDatabase = value ;
}
}
[
ResCategoryAttribute ( Res . DataCategory_Xml ) ,
ResDescriptionAttribute ( Res . SqlParameter_XmlSchemaCollectionOwningSchema ) ,
]
public string XmlSchemaCollectionOwningSchema {
get {
string xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema ;
return ( ( xmlSchemaCollectionOwningSchema ! = null ) ? xmlSchemaCollectionOwningSchema : ADP . StrEmpty ) ;
}
set {
_xmlSchemaCollectionOwningSchema = value ;
}
}
[
ResCategoryAttribute ( Res . DataCategory_Xml ) ,
ResDescriptionAttribute ( Res . SqlParameter_XmlSchemaCollectionName ) ,
]
public string XmlSchemaCollectionName {
get {
string xmlSchemaCollectionName = _xmlSchemaCollectionName ;
return ( ( xmlSchemaCollectionName ! = null ) ? xmlSchemaCollectionName : ADP . StrEmpty ) ;
}
set {
_xmlSchemaCollectionName = value ;
}
}
[
DefaultValue ( false ) ,
ResCategoryAttribute ( Res . DataCategory_Data ) ,
ResDescriptionAttribute ( Res . TCE_SqlParameter_ForceColumnEncryption ) ,
]
public bool ForceColumnEncryption {
get ;
set ;
}
override public DbType DbType {
get {
return GetMetaTypeOnly ( ) . DbType ;
}
set {
MetaType metatype = _metaType ;
if ( ( null = = metatype ) | | ( metatype . DbType ! = value ) | |
// SQLBU 504029: Two special datetime cases for backward compat
// DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead
value = = DbType . Date | |
value = = DbType . Time ) {
PropertyTypeChanging ( ) ;
_metaType = MetaType . GetMetaTypeFromDbType ( value ) ;
}
}
}
public override void ResetDbType ( ) {
ResetSqlDbType ( ) ;
}
internal MetaType InternalMetaType {
get {
Debug . Assert ( null ! = _internalMetaType , "null InternalMetaType" ) ;
return _internalMetaType ;
}
set { _internalMetaType = value ; }
}
[
Browsable ( false ) ,
]
public int LocaleId {
// Lowest 20 bits represent LocaleId
get {
SqlCollation collation = _collation ;
if ( null ! = collation ) {
return collation . LCID ;
}
return 0 ;
}
set {
SqlCollation collation = _collation ;
if ( null = = collation ) {
_collation = collation = new SqlCollation ( ) ;
}
if ( value ! = ( SqlCollation . MaskLcid & value ) ) {
throw ADP . ArgumentOutOfRange ( "LocaleId" ) ;
}
collation . LCID = value ;
}
}
private SqlMetaData MetaData {
get {
MetaType mt = GetMetaTypeOnly ( ) ;
long maxlen ;
if ( mt . IsFixed ) {
maxlen = ( long ) mt . FixedLength ;
}
else if ( Size > 0 | | Size < 0 ) {
maxlen = Size ; // Bug Fix: 302768, 302695, 302694, 302693
}
else {
maxlen = MSS . SmiMetaData . GetDefaultForType ( mt . SqlDbType ) . MaxLength ;
}
return new SqlMetaData ( this . ParameterName , mt . SqlDbType , maxlen , GetActualPrecision ( ) , GetActualScale ( ) , LocaleId , CompareInfo ,
XmlSchemaCollectionDatabase , XmlSchemaCollectionOwningSchema , XmlSchemaCollectionName , mt . IsPlp , _udtType ) ;
}
}
internal bool SizeInferred {
get {
return 0 = = _size ;
}
}
/// <summary>
/// Get SMI Metadata to write out type_info stream.
/// </summary>
/// <returns></returns>
internal MSS . SmiParameterMetaData GetMetadataForTypeInfo ( ) {
ParameterPeekAheadValue peekAhead = null ;
if ( _internalMetaType = = null ) {
_internalMetaType = GetMetaTypeOnly ( ) ;
}
return MetaDataForSmi ( out peekAhead ) ;
}
// IMPORTANT DEVNOTE: This method is being used for parameter encryption functionality, to get the type_info TDS object from SqlParameter.
// Please consider impact to that when changing this method. Refer to the callers of SqlParameter.GetMetadataForTypeInfo().
internal MSS . SmiParameterMetaData MetaDataForSmi ( out ParameterPeekAheadValue peekAhead ) {
peekAhead = null ;
MetaType mt = ValidateTypeLengths ( true /* Yukon or newer */ ) ;
long actualLen = GetActualSize ( ) ;
long maxLen = this . Size ;
// GetActualSize returns bytes length, but smi expects char length for
// character types, so adjust
if ( ! mt . IsLong ) {
if ( SqlDbType . NChar = = mt . SqlDbType | | SqlDbType . NVarChar = = mt . SqlDbType ) {
actualLen = actualLen / sizeof ( char ) ;
}
if ( actualLen > maxLen ) {
maxLen = actualLen ;
}
}
// Determine maxLength for types that ValidateTypeLengths won't figure out
if ( 0 = = maxLen ) {
if ( SqlDbType . Binary = = mt . SqlDbType | | SqlDbType . VarBinary = = mt . SqlDbType ) {
maxLen = MSS . SmiMetaData . MaxBinaryLength ;
}
else if ( SqlDbType . Char = = mt . SqlDbType | | SqlDbType . VarChar = = mt . SqlDbType ) {
maxLen = MSS . SmiMetaData . MaxANSICharacters ;
}
else if ( SqlDbType . NChar = = mt . SqlDbType | | SqlDbType . NVarChar = = mt . SqlDbType ) {
maxLen = MSS . SmiMetaData . MaxUnicodeCharacters ;
}
}
else if ( ( maxLen > MSS . SmiMetaData . MaxBinaryLength & & ( SqlDbType . Binary = = mt . SqlDbType | | SqlDbType . VarBinary = = mt . SqlDbType ) )
| | ( maxLen > MSS . SmiMetaData . MaxANSICharacters & & ( SqlDbType . Char = = mt . SqlDbType | | SqlDbType . VarChar = = mt . SqlDbType ) )
| | ( maxLen > MSS . SmiMetaData . MaxUnicodeCharacters & & ( SqlDbType . NChar = = mt . SqlDbType | | SqlDbType . NVarChar = = mt . SqlDbType ) ) ) {
maxLen = - 1 ;
}
int localeId = LocaleId ;
if ( 0 = = localeId & & mt . IsCharType ) {
object value = GetCoercedValue ( ) ;
if ( value is SqlString & & ! ( ( SqlString ) value ) . IsNull ) {
localeId = ( ( SqlString ) value ) . LCID ;
}
else {
localeId = System . Globalization . CultureInfo . CurrentCulture . LCID ;
}
}
SqlCompareOptions compareOpts = CompareInfo ;
if ( 0 = = compareOpts & & mt . IsCharType ) {
object value = GetCoercedValue ( ) ;
if ( value is SqlString & & ! ( ( SqlString ) value ) . IsNull ) {
compareOpts = ( ( SqlString ) value ) . SqlCompareOptions ;
}
else {
compareOpts = MSS . SmiMetaData . GetDefaultForType ( mt . SqlDbType ) . CompareOptions ;
}
}
string typeSpecificNamePart1 = null ;
string typeSpecificNamePart2 = null ;
string typeSpecificNamePart3 = null ;
if ( SqlDbType . Xml = = mt . SqlDbType ) {
typeSpecificNamePart1 = this . XmlSchemaCollectionDatabase ;
typeSpecificNamePart2 = this . XmlSchemaCollectionOwningSchema ;
typeSpecificNamePart3 = this . XmlSchemaCollectionName ;
}
else if ( SqlDbType . Udt = = mt . SqlDbType | | ( SqlDbType . Structured = = mt . SqlDbType & & ! ADP . IsEmpty ( this . TypeName ) ) ) {
// Split the input name. The type name is specified as single 3 part name.
// NOTE: ParseTypeName throws if format is incorrect
String [ ] names ;
if ( SqlDbType . Udt = = mt . SqlDbType ) {
names = ParseTypeName ( this . UdtTypeName , true /* is UdtTypeName */ ) ;
}
else {
names = ParseTypeName ( this . TypeName , false /* not UdtTypeName */ ) ;
}
if ( 1 = = names . Length ) {
typeSpecificNamePart3 = names [ 0 ] ;
}
else if ( 2 = = names . Length ) {
typeSpecificNamePart2 = names [ 0 ] ;
typeSpecificNamePart3 = names [ 1 ] ;
}
else if ( 3 = = names . Length ) {
typeSpecificNamePart1 = names [ 0 ] ;
typeSpecificNamePart2 = names [ 1 ] ;
typeSpecificNamePart3 = names [ 2 ] ;
}
else {
throw ADP . ArgumentOutOfRange ( "names" ) ;
}
if ( ( ! ADP . IsEmpty ( typeSpecificNamePart1 ) & & TdsEnums . MAX_SERVERNAME < typeSpecificNamePart1 . Length )
| | ( ! ADP . IsEmpty ( typeSpecificNamePart2 ) & & TdsEnums . MAX_SERVERNAME < typeSpecificNamePart2 . Length )
| | ( ! ADP . IsEmpty ( typeSpecificNamePart3 ) & & TdsEnums . MAX_SERVERNAME < typeSpecificNamePart3 . Length ) ) {
throw ADP . ArgumentOutOfRange ( "names" ) ;
}
}
byte precision = GetActualPrecision ( ) ;
byte scale = GetActualScale ( ) ;
// precision for decimal types may still need adjustment.
if ( SqlDbType . Decimal = = mt . SqlDbType ) {
if ( 0 = = precision ) {
precision = TdsEnums . DEFAULT_NUMERIC_PRECISION ;
}
}
// Sub-field determination
List < SmiExtendedMetaData > fields = null ;
MSS . SmiMetaDataPropertyCollection extendedProperties = null ;
if ( SqlDbType . Structured = = mt . SqlDbType ) {
GetActualFieldsAndProperties ( out fields , out extendedProperties , out peekAhead ) ;
}
return new MSS . SmiParameterMetaData ( mt . SqlDbType ,
maxLen ,
precision ,
scale ,
localeId ,
compareOpts ,
null , // Udt type not used for parameters
SqlDbType . Structured = = mt . SqlDbType ,
fields ,
extendedProperties ,
this . ParameterNameFixed ,
typeSpecificNamePart1 ,
typeSpecificNamePart2 ,
typeSpecificNamePart3 ,
this . Direction ) ;
}
internal bool ParamaterIsSqlType {
get {
return _isSqlParameterSqlType ;
}
set {
_isSqlParameterSqlType = value ;
}
}
[
ResCategoryAttribute ( Res . DataCategory_Data ) ,
ResDescriptionAttribute ( Res . SqlParameter_ParameterName ) ,
]
override public string ParameterName {
get {
string parameterName = _parameterName ;
return ( ( null ! = parameterName ) ? parameterName : ADP . StrEmpty ) ;
}
set {
if ( ADP . IsEmpty ( value ) | | ( value . Length < TdsEnums . MAX_PARAMETER_NAME_LENGTH )
| | ( ( '@' = = value [ 0 ] ) & & ( value . Length < = TdsEnums . MAX_PARAMETER_NAME_LENGTH ) ) ) {
if ( _parameterName ! = value ) {
PropertyChanging ( ) ;
_parameterName = value ;
}
}
else {
throw SQL . InvalidParameterNameLength ( value ) ;
}
}
}
internal string ParameterNameFixed {
get {
string parameterName = ParameterName ;
if ( ( 0 < parameterName . Length ) & & ( '@' ! = parameterName [ 0 ] ) ) {
parameterName = "@" + parameterName ;
}
Debug . Assert ( parameterName . Length < = TdsEnums . MAX_PARAMETER_NAME_LENGTH , "parameter name too long" ) ;
return parameterName ;
}
}
[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 ;
SqlDbType dbtype = GetMetaSqlDbTypeOnly ( ) ;
if ( ( 0 = = precision ) & & ( SqlDbType . Decimal = = dbtype ) ) {
precision = ValuePrecision ( SqlValue ) ;
}
return precision ;
}
set {
SqlDbType sqlDbType = SqlDbType ;
if ( sqlDbType = = SqlDbType . Decimal & & value > TdsEnums . MAX_NUMERIC_PRECISION ) {
throw SQL . PrecisionValueOutOfRange ( value ) ;
}
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 ;
SqlDbType dbtype = GetMetaSqlDbTypeOnly ( ) ;
if ( ( 0 = = scale ) & & ( SqlDbType . Decimal = = dbtype ) ) {
scale = ValueScale ( SqlValue ) ;
}
return scale ;
}
set {
if ( _scale ! = value | | ! _hasScale ) {
PropertyChanging ( ) ;
_scale = value ;
_hasScale = true ;
_actualSize = - 1 ; // Invalidate actual size such that it is re-calculated
}
}
}
private bool ShouldSerializeScale ( ) {
return ( 0 ! = _scale ) ; // V1.0 compat, ignore _hasScale
}
[
RefreshProperties ( RefreshProperties . All ) ,
ResCategoryAttribute ( Res . DataCategory_Data ) ,
ResDescriptionAttribute ( Res . SqlParameter_SqlDbType ) ,
System . Data . Common . DbProviderSpecificTypePropertyAttribute ( true ) ,
]
public SqlDbType SqlDbType {
get {
return GetMetaTypeOnly ( ) . SqlDbType ;
}
set {
MetaType metatype = _metaType ;
// HACK!!!
// We didn't want to expose SmallVarBinary on SqlDbType so we
// stuck it at the end of SqlDbType in v1.0, except that now
// we have new data types after that and it's smack dab in the
// middle of the valid range. To prevent folks from setting
// this invalid value we have to have this code here until we
// can take the time to fix it later.
if ( ( SqlDbType ) TdsEnums . SmallVarBinary = = value ) {
throw SQL . InvalidSqlDbType ( value ) ;
}
if ( ( null = = metatype ) | | ( metatype . SqlDbType ! = value ) ) {
PropertyTypeChanging ( ) ;
_metaType = MetaType . GetMetaTypeFromSqlDbType ( value , value = = SqlDbType . Structured ) ;
}
}
}
private bool ShouldSerializeSqlDbType ( ) {
return ( null ! = _metaType ) ;
}
public void ResetSqlDbType ( ) {
if ( null ! = _metaType ) {
PropertyTypeChanging ( ) ;
_metaType = null ;
}
}
[
Browsable ( false ) ,
DesignerSerializationVisibility ( DesignerSerializationVisibility . Hidden ) ,
]
public object SqlValue {
get {
if ( _udtLoadError ! = null ) { // SQL BU DT 329981
throw _udtLoadError ;
}
if ( _value ! = null ) {
if ( _value = = DBNull . Value ) {
return MetaType . GetNullSqlValue ( GetMetaTypeOnly ( ) . SqlType ) ;
}
if ( _value is INullable ) {
return _value ;
}
// SQLBU 503165: for Date and DateTime2, return the CLR object directly without converting it to a SqlValue
// SQLBU 527900: GetMetaTypeOnly() will convert _value to a string in the case of char or char[], so only check
// the SqlDbType for DateTime. This is the only case when we might return the CLR value directly.
if ( _value is DateTime ) {
SqlDbType sqlDbType = GetMetaTypeOnly ( ) . SqlDbType ;
if ( sqlDbType = = SqlDbType . Date | | sqlDbType = = SqlDbType . DateTime2 ) {
return _value ;
}
}
return ( MetaType . GetSqlValueFromComVariant ( _value ) ) ;
}
else if ( _sqlBufferReturnValue ! = null ) {
return _sqlBufferReturnValue . SqlValue ;
}
return null ;
}
set {
Value = value ;
}
}
[
Browsable ( false ) ,
EditorBrowsableAttribute ( EditorBrowsableState . Advanced )
]
public String UdtTypeName {
get {
string typeName = _udtTypeName ;
return ( ( null ! = typeName ) ? typeName : ADP . StrEmpty ) ;
}
set {
_udtTypeName = value ;
}
}
[
Browsable ( false ) ,
EditorBrowsableAttribute ( EditorBrowsableState . Advanced )
]
public String TypeName {
get {
string typeName = _typeName ;
return ( ( null ! = typeName ) ? typeName : ADP . StrEmpty ) ;
}
set {
_typeName = value ;
}
}
[
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 {
if ( _udtLoadError ! = null ) { // SQL BU DT 329981
throw _udtLoadError ;
}
if ( _value ! = null ) {
return _value ;
}
else if ( _sqlBufferReturnValue ! = null ) {
if ( ParamaterIsSqlType ) {
return _sqlBufferReturnValue . SqlValue ;
}
return _sqlBufferReturnValue . Value ;
}
return null ;
}
set {
_value = value ;
_sqlBufferReturnValue = null ;
_coercedValue = null ;
_valueAsINullable = _value as INullable ;
_isSqlParameterSqlType = ( _valueAsINullable ! = null ) ;
_isNull = ( ( _value = = null ) | | ( _value = = DBNull . Value ) | | ( ( _isSqlParameterSqlType ) & & ( _valueAsINullable . IsNull ) ) ) ;
_udtLoadError = null ;
_actualSize = - 1 ;
}
}
internal INullable ValueAsINullable {
get {
return _valueAsINullable ;
}
}
internal bool IsNull {
get {
// NOTE: Udts can change their value any time
if ( _internalMetaType . SqlDbType = = Data . SqlDbType . Udt ) {
_isNull = ( ( _value = = null ) | | ( _value = = DBNull . Value ) | | ( ( _isSqlParameterSqlType ) & & ( _valueAsINullable . IsNull ) ) ) ;
}
return _isNull ;
}
}
//
// always returns data in bytes - except for non-unicode chars, which will be in number of chars
//
internal int GetActualSize ( ) {
MetaType mt = InternalMetaType ;
SqlDbType actualType = mt . SqlDbType ;
// NOTE: Users can change the Udt at any time, so we may need to recalculate
if ( ( _actualSize = = - 1 ) | | ( actualType = = Data . SqlDbType . Udt ) ) {
_actualSize = 0 ;
object val = GetCoercedValue ( ) ;
bool isSqlVariant = false ;
//
if ( IsNull & & ! mt . IsVarTime ) {
return 0 ;
}
// if this is a backend SQLVariant type, then infer the TDS type from the SQLVariant type
if ( actualType = = SqlDbType . Variant ) {
mt = MetaType . GetMetaTypeFromValue ( val , streamAllowed : false ) ;
actualType = MetaType . GetSqlDataType ( mt . TDSType , 0 /*no user type*/ , 0 /*non-nullable type*/ ) . SqlDbType ;
isSqlVariant = true ;
}
if ( mt . IsFixed ) {
_actualSize = mt . FixedLength ;
}
else {
// @hack: until we have ForceOffset behavior we have the following semantics:
// @hack: if the user supplies a Size through the Size propeprty or constructor,
// @hack: we only send a MAX of Size bytes over. If the actualSize is < Size, then
// @hack: we send over actualSize
int coercedSize = 0 ;
// get the actual length of the data, in bytes
switch ( actualType ) {
case SqlDbType . NChar :
case SqlDbType . NVarChar :
case SqlDbType . NText :
case SqlDbType . Xml :
{
coercedSize = ( ( ! _isNull ) & & ( ! _coercedValueIsDataFeed ) ) ? ( StringSize ( val , _coercedValueIsSqlType ) ) : 0 ;
_actualSize = ( ShouldSerializeSize ( ) ? Size : 0 ) ;
_actualSize = ( ( ShouldSerializeSize ( ) & & ( _actualSize < = coercedSize ) ) ? _actualSize : coercedSize ) ;
if ( _actualSize = = - 1 )
_actualSize = coercedSize ;
_actualSize < < = 1 ;
}
break ;
case SqlDbType . Char :
case SqlDbType . VarChar :
case SqlDbType . Text :
{
// for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size
coercedSize = ( ( ! _isNull ) & & ( ! _coercedValueIsDataFeed ) ) ? ( StringSize ( val , _coercedValueIsSqlType ) ) : 0 ;
_actualSize = ( ShouldSerializeSize ( ) ? Size : 0 ) ;
_actualSize = ( ( ShouldSerializeSize ( ) & & ( _actualSize < = coercedSize ) ) ? _actualSize : coercedSize ) ;
if ( _actualSize = = - 1 )
_actualSize = coercedSize ;
}
break ;
case SqlDbType . Binary :
case SqlDbType . VarBinary :
case SqlDbType . Image :
case SqlDbType . Timestamp :
coercedSize = ( ( ! _isNull ) & & ( ! _coercedValueIsDataFeed ) ) ? ( BinarySize ( val , _coercedValueIsSqlType ) ) : 0 ;
_actualSize = ( ShouldSerializeSize ( ) ? Size : 0 ) ;
_actualSize = ( ( ShouldSerializeSize ( ) & & ( _actualSize < = coercedSize ) ) ? _actualSize : coercedSize ) ;
if ( _actualSize = = - 1 )
_actualSize = coercedSize ;
break ;
case SqlDbType . Udt :
//we assume that the object is UDT
if ( ! IsNull ) {
//call the static function
coercedSize = AssemblyCache . GetLength ( val ) ;
}
break ;
case SqlDbType . Structured :
coercedSize = - 1 ;
break ;
case SqlDbType . Time :
_actualSize = ( isSqlVariant ? 5 : MetaType . GetTimeSizeFromScale ( GetActualScale ( ) ) ) ;
break ;
case SqlDbType . DateTime2 :
// Date in number of days (3 bytes) + time
_actualSize = 3 + ( isSqlVariant ? 5 : MetaType . GetTimeSizeFromScale ( GetActualScale ( ) ) ) ;
break ;
case SqlDbType . DateTimeOffset :
// Date in days (3 bytes) + offset in minutes (2 bytes) + time
_actualSize = 5 + ( isSqlVariant ? 5 : MetaType . GetTimeSizeFromScale ( GetActualScale ( ) ) ) ;
break ;
default :
Debug . Assert ( false , "Unknown variable length type!" ) ;
break ;
} // switch
// don't even send big values over to the variant
if ( isSqlVariant & & ( coercedSize > TdsEnums . TYPE_SIZE_LIMIT ) )
throw SQL . ParameterInvalidVariant ( this . ParameterName ) ;
}
}
return _actualSize ;
}
object ICloneable . Clone ( ) {
return new SqlParameter ( this ) ;
}
// Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata)
internal static object CoerceValue ( object value , MetaType destinationType , out bool coercedToDataFeed , out bool typeChanged , bool allowStreaming = true ) {
Debug . Assert ( ! ( value is DataFeed ) , "Value provided should not already be a data feed" ) ;
Debug . Assert ( ! ADP . IsNull ( value ) , "Value provided should not be null" ) ;
Debug . Assert ( null ! = destinationType , "null destinationType" ) ;
coercedToDataFeed = false ;
typeChanged = false ;
Type currentType = value . GetType ( ) ;
if ( ( typeof ( object ) ! = destinationType . ClassType ) & &
( currentType ! = destinationType . ClassType ) & &
( ( currentType ! = destinationType . SqlType ) | | ( SqlDbType . Xml = = destinationType . SqlDbType ) ) ) { // Special case for Xml types (since we need to convert SqlXml into a string)
try {
// Assume that the type changed
typeChanged = true ;
if ( ( typeof ( string ) = = destinationType . ClassType ) ) {
// For Xml data, destination Type is always string
if ( typeof ( SqlXml ) = = currentType ) {
value = MetaType . GetStringFromXml ( ( XmlReader ) ( ( ( SqlXml ) value ) . CreateReader ( ) ) ) ;
}
else if ( typeof ( SqlString ) = = currentType ) {
typeChanged = false ; // Do nothing
}
else if ( typeof ( XmlReader ) . IsAssignableFrom ( currentType ) ) {
if ( allowStreaming ) {
coercedToDataFeed = true ;
value = new XmlDataFeed ( ( XmlReader ) value ) ;
}
else {
value = MetaType . GetStringFromXml ( ( XmlReader ) value ) ;
}
}
else if ( typeof ( char [ ] ) = = currentType ) {
value = new string ( ( char [ ] ) value ) ;
}
else if ( typeof ( SqlChars ) = = currentType ) {
value = new string ( ( ( SqlChars ) value ) . Value ) ;
}
else if ( value is TextReader & & allowStreaming ) {
coercedToDataFeed = true ;
value = new TextDataFeed ( ( TextReader ) value ) ;
}
else {
value = Convert . ChangeType ( value , destinationType . ClassType , ( IFormatProvider ) null ) ;
}
}
else if ( ( DbType . Currency = = destinationType . DbType ) & & ( typeof ( string ) = = currentType ) ) {
value = Decimal . Parse ( ( string ) value , NumberStyles . Currency , ( IFormatProvider ) null ) ; // WebData 99376
}
else if ( ( typeof ( SqlBytes ) = = currentType ) & & ( typeof ( byte [ ] ) = = destinationType . ClassType ) ) {
typeChanged = false ; // Do nothing
}
else if ( ( typeof ( string ) = = currentType ) & & ( SqlDbType . Time = = destinationType . SqlDbType ) ) {
value = TimeSpan . Parse ( ( string ) value ) ;
}
else if ( ( typeof ( string ) = = currentType ) & & ( SqlDbType . DateTimeOffset = = destinationType . SqlDbType ) ) {
value = DateTimeOffset . Parse ( ( string ) value , ( IFormatProvider ) null ) ;
}
else if ( ( typeof ( DateTime ) = = currentType ) & & ( SqlDbType . DateTimeOffset = = destinationType . SqlDbType ) ) {
value = new DateTimeOffset ( ( DateTime ) value ) ;
}
else if ( TdsEnums . SQLTABLE = = destinationType . TDSType & &
( value is DataTable | |
value is DbDataReader | |
value is System . Collections . Generic . IEnumerable < SqlDataRecord > ) ) {
// no conversion for TVPs.
typeChanged = false ;
}
else if ( destinationType . ClassType = = typeof ( byte [ ] ) & & value is Stream & & allowStreaming ) {
coercedToDataFeed = true ;
value = new StreamDataFeed ( ( Stream ) value ) ;
}
else {
value = Convert . ChangeType ( value , destinationType . ClassType , ( IFormatProvider ) null ) ;
}
}
catch ( Exception e ) {
//
if ( ! ADP . IsCatchableExceptionType ( e ) ) {
throw ;
}
throw ADP . ParameterConversionFailed ( value , destinationType . ClassType , e ) ; // WebData 75433
}
}
Debug . Assert ( allowStreaming | | ! coercedToDataFeed , "Streaming is not allowed, but type was coerced into a data feed" ) ;
Debug . Assert ( value . GetType ( ) = = currentType ^ typeChanged , "Incorrect value for typeChanged" ) ;
return value ;
}
internal void FixStreamDataForNonPLP ( ) {
object value = GetCoercedValue ( ) ;
AssertCachedPropertiesAreValid ( ) ;
if ( ! _coercedValueIsDataFeed ) {
return ;
}
_coercedValueIsDataFeed = false ;
if ( value is TextDataFeed ) {
if ( Size > 0 ) {
char [ ] buffer = new char [ Size ] ;
int nRead = ( ( TextDataFeed ) value ) . _source . ReadBlock ( buffer , 0 , Size ) ;
CoercedValue = new string ( buffer , 0 , nRead ) ;
}
else {
CoercedValue = ( ( TextDataFeed ) value ) . _source . ReadToEnd ( ) ;
}
return ;
}
if ( value is StreamDataFeed ) {
if ( Size > 0 ) {
byte [ ] buffer = new byte [ Size ] ;
int totalRead = 0 ;
Stream sourceStream = ( ( StreamDataFeed ) value ) . _source ;
while ( totalRead < Size ) {
int nRead = sourceStream . Read ( buffer , totalRead , Size - totalRead ) ;
if ( nRead = = 0 ) {
break ;
}
totalRead + = nRead ;
}
if ( totalRead < Size ) {
Array . Resize ( ref buffer , totalRead ) ;
}
CoercedValue = buffer ;
}
else {
MemoryStream ms = new MemoryStream ( ) ;
( ( StreamDataFeed ) value ) . _source . CopyTo ( ms ) ;
CoercedValue = ms . ToArray ( ) ;
}
return ;
}
if ( value is XmlDataFeed ) {
CoercedValue = MetaType . GetStringFromXml ( ( ( XmlDataFeed ) value ) . _source ) ;
return ;
}
// We should have returned before reaching here
Debug . Assert ( false , "_coercedValueIsDataFeed was true, but the value was not a known DataFeed type" ) ;
}
private void CloneHelper ( SqlParameter destination ) {
CloneHelperCore ( destination ) ;
destination . _metaType = _metaType ;
destination . _collation = _collation ;
destination . _xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase ;
destination . _xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema ;
destination . _xmlSchemaCollectionName = _xmlSchemaCollectionName ;
destination . _udtTypeName = _udtTypeName ;
destination . _typeName = _typeName ;
destination . _udtLoadError = _udtLoadError ;
destination . _parameterName = _parameterName ;
destination . _precision = _precision ;
destination . _scale = _scale ;
destination . _sqlBufferReturnValue = _sqlBufferReturnValue ;
destination . _isSqlParameterSqlType = _isSqlParameterSqlType ;
destination . _internalMetaType = _internalMetaType ;
destination . CoercedValue = CoercedValue ; // copy cached value reference because of XmlReader problem
destination . _valueAsINullable = _valueAsINullable ;
destination . _isNull = _isNull ;
destination . _coercedValueIsDataFeed = _coercedValueIsDataFeed ;
destination . _coercedValueIsSqlType = _coercedValueIsSqlType ;
destination . _actualSize = _actualSize ;
destination . ForceColumnEncryption = ForceColumnEncryption ;
}
internal byte GetActualPrecision ( ) {
return ShouldSerializePrecision ( ) ? PrecisionInternal : ValuePrecision ( CoercedValue ) ;
}
internal byte GetActualScale ( ) {
if ( ShouldSerializeScale ( ) ) {
return ScaleInternal ;
}
// issue: how could a user specify 0 as the actual scale?
if ( GetMetaTypeOnly ( ) . IsVarTime ) {
return TdsEnums . DEFAULT_VARTIME_SCALE ;
}
return ValueScale ( CoercedValue ) ;
}
internal int GetParameterSize ( ) {
return ShouldSerializeSize ( ) ? Size : ValueSize ( CoercedValue ) ;
}
private void GetActualFieldsAndProperties ( out List < MSS . SmiExtendedMetaData > fields , out SmiMetaDataPropertyCollection props , out ParameterPeekAheadValue peekAhead ) {
fields = null ;
props = null ;
peekAhead = null ;
object value = GetCoercedValue ( ) ;
if ( value is DataTable ) {
DataTable dt = value as DataTable ;
if ( dt . Columns . Count < = 0 ) {
throw SQL . NotEnoughColumnsInStructuredType ( ) ;
}
fields = new List < MSS . SmiExtendedMetaData > ( dt . Columns . Count ) ;
bool [ ] keyCols = new bool [ dt . Columns . Count ] ;
bool hasKey = false ;
// set up primary key as unique key list
// do this prior to general metadata loop to favor the primary key
if ( null ! = dt . PrimaryKey & & 0 < dt . PrimaryKey . Length ) {
foreach ( DataColumn col in dt . PrimaryKey ) {
keyCols [ col . Ordinal ] = true ;
hasKey = true ;
}
}
for ( int i = 0 ; i < dt . Columns . Count ; i + + ) {
fields . Add ( MSS . MetaDataUtilsSmi . SmiMetaDataFromDataColumn ( dt . Columns [ i ] , dt ) ) ;
// DataColumn uniqueness is only for a single column, so don't add
// more than one. (keyCols.Count first for assumed minimal perf benefit)
if ( ! hasKey & & dt . Columns [ i ] . Unique ) {
keyCols [ i ] = true ;
hasKey = true ;
}
}
// Add unique key property, if any found.
if ( hasKey ) {
props = new SmiMetaDataPropertyCollection ( ) ;
props [ MSS . SmiPropertySelector . UniqueKey ] = new MSS . SmiUniqueKeyProperty ( new List < bool > ( keyCols ) ) ;
}
}
else if ( value is SqlDataReader ) {
fields = new List < MSS . SmiExtendedMetaData > ( ( ( SqlDataReader ) value ) . GetInternalSmiMetaData ( ) ) ;
if ( fields . Count < = 0 ) {
throw SQL . NotEnoughColumnsInStructuredType ( ) ;
}
bool [ ] keyCols = new bool [ fields . Count ] ;
bool hasKey = false ;
for ( int i = 0 ; i < fields . Count ; i + + ) {
MSS . SmiQueryMetaData qmd = fields [ i ] as MSS . SmiQueryMetaData ;
if ( null ! = qmd & & ! qmd . IsKey . IsNull & & qmd . IsKey . Value ) {
keyCols [ i ] = true ;
hasKey = true ;
}
}
// Add unique key property, if any found.
if ( hasKey ) {
props = new SmiMetaDataPropertyCollection ( ) ;
props [ MSS . SmiPropertySelector . UniqueKey ] = new MSS . SmiUniqueKeyProperty ( new List < bool > ( keyCols ) ) ;
}
}
else if ( value is IEnumerable < SqlDataRecord > ) {
// must grab the first record of the enumerator to get the metadata
IEnumerator < MSS . SqlDataRecord > enumerator = ( ( IEnumerable < MSS . SqlDataRecord > ) value ) . GetEnumerator ( ) ;
MSS . SqlDataRecord firstRecord = null ;
try {
// no need for fields if there's no rows or no columns -- we'll be sending a null instance anyway.
if ( enumerator . MoveNext ( ) ) {
firstRecord = enumerator . Current ;
int fieldCount = firstRecord . FieldCount ;
if ( 0 < fieldCount ) {
// It's valid! Grab those fields.
bool [ ] keyCols = new bool [ fieldCount ] ;
bool [ ] defaultFields = new bool [ fieldCount ] ;
bool [ ] sortOrdinalSpecified = new bool [ fieldCount ] ;
int maxSortOrdinal = - 1 ; // largest sort ordinal seen, used to optimize locating holes in the list
bool hasKey = false ;
bool hasDefault = false ;
int sortCount = 0 ;
SmiOrderProperty . SmiColumnOrder [ ] sort = new SmiOrderProperty . SmiColumnOrder [ fieldCount ] ;
fields = new List < MSS . SmiExtendedMetaData > ( fieldCount ) ;
for ( int i = 0 ; i < fieldCount ; i + + ) {
SqlMetaData colMeta = firstRecord . GetSqlMetaData ( i ) ;
fields . Add ( MSS . MetaDataUtilsSmi . SqlMetaDataToSmiExtendedMetaData ( colMeta ) ) ;
if ( colMeta . IsUniqueKey ) {
keyCols [ i ] = true ;
hasKey = true ;
}
if ( colMeta . UseServerDefault ) {
defaultFields [ i ] = true ;
hasDefault = true ;
}
sort [ i ] . Order = colMeta . SortOrder ;
if ( SortOrder . Unspecified ! = colMeta . SortOrder ) {
// SqlMetaData takes care of checking for negative sort ordinals with specified sort order
// bail early if there's no way sort order could be monotonically increasing
if ( fieldCount < = colMeta . SortOrdinal ) {
throw SQL . SortOrdinalGreaterThanFieldCount ( i , colMeta . SortOrdinal ) ;
}
// Check to make sure we haven't seen this ordinal before
if ( sortOrdinalSpecified [ colMeta . SortOrdinal ] ) {
throw SQL . DuplicateSortOrdinal ( colMeta . SortOrdinal ) ;
}
sort [ i ] . SortOrdinal = colMeta . SortOrdinal ;
sortOrdinalSpecified [ colMeta . SortOrdinal ] = true ;
if ( colMeta . SortOrdinal > maxSortOrdinal ) {
maxSortOrdinal = colMeta . SortOrdinal ;
}
sortCount + + ;
}
}
if ( hasKey ) {
props = new SmiMetaDataPropertyCollection ( ) ;
props [ MSS . SmiPropertySelector . UniqueKey ] = new MSS . SmiUniqueKeyProperty ( new List < bool > ( keyCols ) ) ;
}
if ( hasDefault ) {
// May have already created props list in unique key handling
if ( null = = props ) {
props = new SmiMetaDataPropertyCollection ( ) ;
}
props [ MSS . SmiPropertySelector . DefaultFields ] = new MSS . SmiDefaultFieldsProperty ( new List < bool > ( defaultFields ) ) ;
}
if ( 0 < sortCount ) {
// validate monotonically increasing sort order.
// Since we already checked for duplicates, we just need
// to watch for values outside of the sortCount range.
if ( maxSortOrdinal > = sortCount ) {
// there is at least one hole, find the first one
int i ;
for ( i = 0 ; i < sortCount ; i + + ) {
if ( ! sortOrdinalSpecified [ i ] ) {
break ;
}
}
Debug . Assert ( i < sortCount , "SqlParameter.GetActualFieldsAndProperties: SortOrdinal hole-finding algorithm failed!" ) ;
throw SQL . MissingSortOrdinal ( i ) ;
}
// May have already created props list
if ( null = = props ) {
props = new SmiMetaDataPropertyCollection ( ) ;
}
props [ MSS . SmiPropertySelector . SortOrder ] = new MSS . SmiOrderProperty (
new List < SmiOrderProperty . SmiColumnOrder > ( sort ) ) ;
}
// pack it up so we don't have to rewind to send the first value
peekAhead = new ParameterPeekAheadValue ( ) ;
peekAhead . Enumerator = enumerator ;
peekAhead . FirstRecord = firstRecord ;
// now that it's all packaged, make sure we don't dispose it.
enumerator = null ;
}
else {
throw SQL . NotEnoughColumnsInStructuredType ( ) ;
}
}
else {
throw SQL . IEnumerableOfSqlDataRecordHasNoRows ( ) ;
}
}
finally {
if ( enumerator ! = null ) {
enumerator . Dispose ( ) ;
}
}
}
else if ( value is DbDataReader ) {
DataTable schema = ( ( DbDataReader ) value ) . GetSchemaTable ( ) ;
if ( schema . Rows . Count < = 0 ) {
throw SQL . NotEnoughColumnsInStructuredType ( ) ;
}
int fieldCount = schema . Rows . Count ;
fields = new List < MSS . SmiExtendedMetaData > ( fieldCount ) ;
bool [ ] keyCols = new bool [ fieldCount ] ;
bool hasKey = false ;
int ordinalForIsKey = schema . Columns [ SchemaTableColumn . IsKey ] . Ordinal ;
int ordinalForColumnOrdinal = schema . Columns [ SchemaTableColumn . ColumnOrdinal ] . Ordinal ;
// Extract column metadata
for ( int rowOrdinal = 0 ; rowOrdinal < fieldCount ; rowOrdinal + + ) {
DataRow row = schema . Rows [ rowOrdinal ] ;
SmiExtendedMetaData candidateMd = MSS . MetaDataUtilsSmi . SmiMetaDataFromSchemaTableRow ( row ) ;
// Determine destination ordinal. Allow for ordinal not specified by assuming rowOrdinal *is* columnOrdinal
// in that case, but don't worry about mix-and-match of the two techniques
int columnOrdinal = rowOrdinal ;
if ( ! row . IsNull ( ordinalForColumnOrdinal ) ) {
columnOrdinal = ( int ) row [ ordinalForColumnOrdinal ] ;
}
// After this point, things we are creating (keyCols, fields) should be accessed by columnOrdinal
// while the source should just be accessed via "row".
// Watch for out-of-range ordinals
if ( columnOrdinal > = fieldCount | | columnOrdinal < 0 ) {
throw SQL . InvalidSchemaTableOrdinals ( ) ;
}
// extend empty space if out-of-order ordinal
while ( columnOrdinal > fields . Count ) {
fields . Add ( null ) ;
}
// Now add the candidate to the list
if ( fields . Count = = columnOrdinal ) {
fields . Add ( candidateMd ) ;
}
else {
// Disallow two columns using the same ordinal (even if due to mixing null and non-null columnOrdinals)
if ( fields [ columnOrdinal ] ! = null ) {
throw SQL . InvalidSchemaTableOrdinals ( ) ;
}
// Don't use insert, since it shifts all later columns down a notch
fields [ columnOrdinal ] = candidateMd ;
}
// Propogate key information
if ( ! row . IsNull ( ordinalForIsKey ) & & ( bool ) row [ ordinalForIsKey ] ) {
keyCols [ columnOrdinal ] = true ;
hasKey = true ;
}
}
#if DEBUG
// Check for holes
// Above loop logic prevents holes since:
// 1) loop processes fieldcount # of columns
// 2) no ordinals outside continuous range from 0 to fieldcount - 1 are allowed
// 3) no duplicate ordinals are allowed
// But assert no holes to be sure.
foreach ( SmiExtendedMetaData md in fields ) {
Debug . Assert ( null ! = md , "Shouldn't be able to have holes, since original loop algorithm prevents such." ) ;
}
#endif
// Add unique key property, if any defined.
if ( hasKey ) {
props = new SmiMetaDataPropertyCollection ( ) ;
props [ MSS . SmiPropertySelector . UniqueKey ] = new MSS . SmiUniqueKeyProperty ( new List < bool > ( keyCols ) ) ;
}
}
}
internal object GetCoercedValue ( ) {
// NOTE: User can change the Udt at any time
if ( ( null = = _coercedValue ) | | ( _internalMetaType . SqlDbType = = Data . SqlDbType . Udt ) ) { // will also be set during parameter Validation
bool isDataFeed = Value is DataFeed ;
if ( ( IsNull ) | | ( isDataFeed ) ) {
// No coercion is done for DataFeeds and Nulls
_coercedValue = Value ;
_coercedValueIsSqlType = ( _coercedValue = = null ) ? false : _isSqlParameterSqlType ; // set to null for output parameters that keeps _isSqlParameterSqlType
_coercedValueIsDataFeed = isDataFeed ;
_actualSize = IsNull ? 0 : - 1 ;
}
else {
bool typeChanged ;
_coercedValue = CoerceValue ( Value , _internalMetaType , out _coercedValueIsDataFeed , out typeChanged ) ;
_coercedValueIsSqlType = ( ( _isSqlParameterSqlType ) & & ( ! typeChanged ) ) ; // Type changed always results in a CLR type
_actualSize = - 1 ;
}
}
AssertCachedPropertiesAreValid ( ) ;
return _coercedValue ;
}
internal bool CoercedValueIsSqlType {
get {
if ( null = = _coercedValue ) {
GetCoercedValue ( ) ;
}
AssertCachedPropertiesAreValid ( ) ;
return _coercedValueIsSqlType ;
}
}
internal bool CoercedValueIsDataFeed {
get {
if ( null = = _coercedValue ) {
GetCoercedValue ( ) ;
}
AssertCachedPropertiesAreValid ( ) ;
return _coercedValueIsDataFeed ;
}
}
[Conditional("DEBUG")]
internal void AssertCachedPropertiesAreValid ( )
{
AssertPropertiesAreValid ( _coercedValue , _coercedValueIsSqlType , _coercedValueIsDataFeed , IsNull ) ;
}
[Conditional("DEBUG")]
internal void AssertPropertiesAreValid ( object value , bool? isSqlType = null , bool? isDataFeed = null , bool? isNull = null )
{
Debug . Assert ( ! isSqlType . HasValue | | ( isSqlType . Value = = ( value is INullable ) ) , "isSqlType is incorrect" ) ;
Debug . Assert ( ! isDataFeed . HasValue | | ( isDataFeed . Value = = ( value is DataFeed ) ) , "isDataFeed is incorrect" ) ;
Debug . Assert ( ! isNull . HasValue | | ( isNull . Value = = ADP . IsNull ( value ) ) , "isNull is incorrect" ) ;
}
private SqlDbType GetMetaSqlDbTypeOnly ( ) {
MetaType metaType = _metaType ;
if ( null = = metaType ) { // infer the type from the value
metaType = MetaType . GetDefaultMetaType ( ) ;
}
return metaType . SqlDbType ;
}
// This may not be a good thing to do in case someone overloads the parameter type but I
// don't want to go from SqlDbType -> metaType -> TDSType
private MetaType GetMetaTypeOnly ( ) {
if ( null ! = _metaType ) {
return _metaType ;
}
if ( null ! = _value & & DBNull . Value ! = _value ) {
// We have a value set by the user then just use that value
// char and char[] are not directly supported so we convert those values to string
Type valueType = _value . GetType ( ) ;
if ( typeof ( char ) = = valueType ) {
_value = _value . ToString ( ) ;
valueType = typeof ( string ) ;
}
else if ( typeof ( char [ ] ) = = valueType ) {
_value = new string ( ( char [ ] ) _value ) ;
valueType = typeof ( string ) ;
}
return MetaType . GetMetaTypeFromType ( valueType ) ;
}
else if ( null ! = _sqlBufferReturnValue ) { // value came back from the server
Type valueType = _sqlBufferReturnValue . GetTypeFromStorageType ( _isSqlParameterSqlType ) ;
if ( null ! = valueType ) {
return MetaType . GetMetaTypeFromType ( valueType ) ;
}
}
return MetaType . GetDefaultMetaType ( ) ;
}
internal void Prepare ( SqlCommand cmd ) { // MDAC 67063
if ( null = = _metaType ) {
throw ADP . PrepareParameterType ( cmd ) ;
}
else if ( ! ShouldSerializeSize ( ) & & ! _metaType . IsFixed ) {
throw ADP . PrepareParameterSize ( cmd ) ;
}
else if ( ( ! ShouldSerializePrecision ( ) & & ! ShouldSerializeScale ( ) ) & & ( _metaType . SqlDbType = = SqlDbType . Decimal ) ) {
throw ADP . PrepareParameterScale ( cmd , SqlDbType . ToString ( ) ) ;
}
}
private void PropertyChanging ( ) {
_internalMetaType = null ;
}
private void PropertyTypeChanging ( ) {
PropertyChanging ( ) ;
CoercedValue = null ;
}
internal void SetSqlBuffer ( SqlBuffer buff ) {
_sqlBufferReturnValue = buff ;
_value = null ;
_coercedValue = null ;
_isNull = _sqlBufferReturnValue . IsNull ;
_coercedValueIsDataFeed = false ;
_coercedValueIsSqlType = false ;
_udtLoadError = null ;
_actualSize = - 1 ;
}
internal void SetUdtLoadError ( Exception e ) { // SQL BU DT 329981
_udtLoadError = e ;
}
internal void Validate ( int index , bool isCommandProc ) {
MetaType metaType = GetMetaTypeOnly ( ) ;
_internalMetaType = metaType ;
// NOTE: (General Criteria): SqlParameter does a Size Validation check and would fail if the size is 0.
// This condition filters all scenarios where we view a valid size 0.
if ( ADP . IsDirection ( this , ParameterDirection . Output ) & &
! ADP . IsDirection ( this , ParameterDirection . ReturnValue ) & & // SQL BU DT 372370
( ! metaType . IsFixed ) & &
! ShouldSerializeSize ( ) & &
( ( null = = _value ) | | Convert . IsDBNull ( _value ) ) & &
( SqlDbType ! = SqlDbType . Timestamp ) & &
( SqlDbType ! = SqlDbType . Udt ) & &
//
( SqlDbType ! = SqlDbType . Xml ) & &
! metaType . IsVarTime ) {
throw ADP . UninitializedParameterSize ( index , metaType . ClassType ) ;
}
if ( metaType . SqlDbType ! = SqlDbType . Udt & & Direction ! = ParameterDirection . Output ) {
GetCoercedValue ( ) ;
}
//check if the UdtTypeName is specified for Udt params
if ( metaType . SqlDbType = = SqlDbType . Udt ) {
if ( ADP . IsEmpty ( UdtTypeName ) )
throw SQL . MustSetUdtTypeNameForUdtParams ( ) ;
}
else if ( ! ADP . IsEmpty ( UdtTypeName ) ) {
throw SQL . UnexpectedUdtTypeNameForNonUdtParams ( ) ;
}
// Validate structured-type-specific details.
if ( metaType . SqlDbType = = SqlDbType . Structured ) {
if ( ! isCommandProc & & ADP . IsEmpty ( TypeName ) )
throw SQL . MustSetTypeNameForParam ( metaType . TypeName , this . ParameterName ) ;
if ( ParameterDirection . Input ! = this . Direction ) {
throw SQL . UnsupportedTVPOutputParameter ( this . Direction , this . ParameterName ) ;
}
if ( DBNull . Value = = GetCoercedValue ( ) ) {
throw SQL . DBNullNotSupportedForTVPValues ( this . ParameterName ) ;
}
}
else if ( ! ADP . IsEmpty ( TypeName ) ) {
throw SQL . UnexpectedTypeNameForNonStructParams ( this . ParameterName ) ;
}
}
// func will change type to that with a 4 byte length if the type has a two
// byte length and a parameter length > than that expressable in 2 bytes
internal MetaType ValidateTypeLengths ( bool yukonOrNewer ) {
MetaType mt = InternalMetaType ;
// MDAC bug #50839 + #52829 : Since the server will automatically reject any
// char, varchar, binary, varbinary, nchar, or nvarchar parameter that has a
// byte sizeInCharacters > 8000 bytes, we promote the parameter to image, text, or ntext. This
// allows the user to specify a parameter type using a COM+ datatype and be able to
// use that parameter against a BLOB column.
if ( ( SqlDbType . Udt ! = mt . SqlDbType ) & & ( false = = mt . IsFixed ) & & ( false = = mt . IsLong ) ) { // if type has 2 byte length
long actualSizeInBytes = this . GetActualSize ( ) ;
long sizeInCharacters = this . Size ;
// Bug: VSTFDevDiv #636867
// Notes:
// 'actualSizeInBytes' is the size of value passed;
// 'sizeInCharacters' is the parameter size;
// 'actualSizeInBytes' is in bytes;
// 'this.Size' is in charaters;
// 'sizeInCharacters' is in characters;
// 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes;
// For Non-NCharType and for non-Yukon or greater variables, size should be maintained;
// Reverting changes from bug VSTFDevDiv # 479739 as it caused an regression;
// Modifed variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and
// 'maxSize' to 'maxSizeInBytes'
// The idea is to
// 1) revert the regression from bug 479739
// 2) fix as many scenarios as possible including bug 636867
// 3) cause no additional regression from 3.5 sp1
// Keeping these goals in mind - the following are the changes we are making
long maxSizeInBytes = 0 ;
if ( ( mt . IsNCharType ) & & ( yukonOrNewer ) )
maxSizeInBytes = ( ( sizeInCharacters * sizeof ( char ) ) > actualSizeInBytes ) ? sizeInCharacters * sizeof ( char ) : actualSizeInBytes ;
else
{
// Notes:
// Elevation from (n)(var)char (4001+) to (n)text succeeds without failure only with Yukon and greater.
// it fails in sql server 2000
maxSizeInBytes = ( sizeInCharacters > actualSizeInBytes ) ? sizeInCharacters : actualSizeInBytes ;
}
if ( ( maxSizeInBytes > TdsEnums . TYPE_SIZE_LIMIT ) | | ( _coercedValueIsDataFeed ) | |
( sizeInCharacters = = - 1 ) | | ( actualSizeInBytes = = - 1 ) ) { // is size > size able to be described by 2 bytes
if ( yukonOrNewer ) {
// Convert the parameter to its max type
mt = MetaType . GetMaxMetaTypeFromMetaType ( mt ) ;
_metaType = mt ;
InternalMetaType = mt ;
if ( ! mt . IsPlp ) {
if ( mt . SqlDbType = = SqlDbType . Xml ) {
throw ADP . InvalidMetaDataValue ( ) ; //Xml should always have IsPartialLength = true
}
if ( mt . SqlDbType = = SqlDbType . NVarChar
| | mt . SqlDbType = = SqlDbType . VarChar
| | mt . SqlDbType = = SqlDbType . VarBinary ) {
Size = ( int ) ( SmiMetaData . UnlimitedMaxLengthIndicator ) ;
}
}
}
else {
switch ( mt . SqlDbType ) { // widening the SqlDbType is automatic
case SqlDbType . Binary :
case SqlDbType . VarBinary :
mt = MetaType . GetMetaTypeFromSqlDbType ( SqlDbType . Image , false ) ;
_metaType = mt ; // do not use SqlDbType property which calls PropertyTypeChanging resetting coerced value
InternalMetaType = mt ;
break ;
case SqlDbType . Char :
case SqlDbType . VarChar :
mt = MetaType . GetMetaTypeFromSqlDbType ( SqlDbType . Text , false ) ;
_metaType = mt ;
InternalMetaType = mt ;
break ;
case SqlDbType . NChar :
case SqlDbType . NVarChar :
mt = MetaType . GetMetaTypeFromSqlDbType ( SqlDbType . NText , false ) ;
_metaType = mt ;
InternalMetaType = mt ;
break ;
default :
Debug . Assert ( false , "Missed metatype in SqlCommand.BuildParamList()" ) ;
break ;
}
}
}
}
return mt ;
}
private byte ValuePrecision ( object value ) {
if ( value is SqlDecimal ) {
if ( ( ( SqlDecimal ) value ) . IsNull ) // MDAC #79648
return 0 ;
return ( ( SqlDecimal ) value ) . Precision ;
}
return ValuePrecisionCore ( value ) ;
}
private byte ValueScale ( object value ) {
if ( value is SqlDecimal ) {
if ( ( ( SqlDecimal ) value ) . IsNull ) // MDAC #79648
return 0 ;
return ( ( SqlDecimal ) value ) . Scale ;
}
return ValueScaleCore ( value ) ;
}
private static int StringSize ( object value , bool isSqlType ) {
if ( isSqlType ) {
Debug . Assert ( ! ( ( INullable ) value ) . IsNull , "Should not call StringSize on null values" ) ;
if ( value is SqlString ) {
return ( ( SqlString ) value ) . Value . Length ;
}
if ( value is SqlChars ) {
return ( ( SqlChars ) value ) . Value . Length ;
}
}
else {
string svalue = ( value as string ) ;
if ( null ! = svalue ) {
return svalue . Length ;
}
char [ ] cvalue = ( value as char [ ] ) ;
if ( null ! = cvalue ) {
return cvalue . Length ;
}
if ( value is char ) {
return 1 ;
}
}
// Didn't match, unknown size
return 0 ;
}
private static int BinarySize ( object value , bool isSqlType ) {
if ( isSqlType ) {
Debug . Assert ( ! ( ( INullable ) value ) . IsNull , "Should not call StringSize on null values" ) ;
if ( value is SqlBinary ) {
return ( ( SqlBinary ) value ) . Length ;
}
if ( value is SqlBytes ) {
return ( ( SqlBytes ) value ) . Value . Length ;
}
}
else {
byte [ ] bvalue = ( value as byte [ ] ) ;
if ( null ! = bvalue ) {
return bvalue . Length ;
}
if ( value is byte ) {
return 1 ;
}
}
// Didn't match, unknown size
return 0 ;
}
private int ValueSize ( object value ) {
if ( value is SqlString ) {
if ( ( ( SqlString ) value ) . IsNull ) // MDAC #79648
return 0 ;
return ( ( SqlString ) value ) . Value . Length ;
}
if ( value is SqlChars ) {
if ( ( ( SqlChars ) value ) . IsNull )
return 0 ;
return ( ( SqlChars ) value ) . Value . Length ;
}
if ( value is SqlBinary ) {
if ( ( ( SqlBinary ) value ) . IsNull ) // MDAC #79648
return 0 ;
return ( ( SqlBinary ) value ) . Length ;
}
if ( value is SqlBytes ) {
if ( ( ( SqlBytes ) value ) . IsNull )
return 0 ;
return ( int ) ( ( ( SqlBytes ) value ) . Length ) ;
}
if ( value is DataFeed )
{
// Unknown length
return 0 ;
}
return ValueSizeCore ( value ) ;
}
//
// parse an string of the form db.schema.name where any of the three components
// might have "[" "]" and dots within it.
// returns:
// [0] dbname (or null)
// [1] schema (or null)
// [2] name
// NOTE: if perf/space implications of Regex is not a problem, we can get rid
// of this and use a simple regex to do the parsing
internal static string [ ] ParseTypeName ( string typeName , bool isUdtTypeName ) {
Debug . Assert ( null ! = typeName , "null typename passed to ParseTypeName" ) ;
try {
string errorMsg ;
if ( isUdtTypeName ) {
errorMsg = Res . SQL_UDTTypeName ;
}
else {
errorMsg = Res . SQL_TypeName ;
}
return MultipartIdentifier . ParseMultipartIdentifier ( typeName , "[\"" , "]\"" , '.' , 3 , true , errorMsg , true ) ;
}
catch ( ArgumentException ) {
if ( isUdtTypeName ) {
throw SQL . InvalidUdt3PartNameFormat ( ) ;
}
else {
throw SQL . InvalidParameterTypeNameFormat ( ) ;
}
}
}
sealed internal class SqlParameterConverter : ExpandableObjectConverter {
// converter classes should have public ctor
public SqlParameterConverter ( ) {
}
override public bool CanConvertTo ( ITypeDescriptorContext context , Type destinationType ) {
if ( typeof ( System . ComponentModel . Design . Serialization . InstanceDescriptor ) = = destinationType ) {
return true ;
}
return base . CanConvertTo ( context , destinationType ) ;
}
override public object ConvertTo ( ITypeDescriptorContext context , CultureInfo culture , object value , Type destinationType ) {
if ( destinationType = = null ) {
throw ADP . ArgumentNull ( "destinationType" ) ;
}
if ( ( typeof ( System . ComponentModel . Design . Serialization . InstanceDescriptor ) = = destinationType ) & & ( value is SqlParameter ) ) {
return ConvertToInstanceDescriptor ( value as SqlParameter ) ;
}
return base . ConvertTo ( context , culture , value , destinationType ) ;
}
private System . ComponentModel . Design . Serialization . InstanceDescriptor ConvertToInstanceDescriptor ( SqlParameter p ) {
// MDAC 67321 - reducing parameter generated code
int flags = 0 ; // if part of the collection - the parametername can't be empty
if ( p . ShouldSerializeSqlDbType ( ) ) {
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 | | ! ADP . IsEmpty ( p . XmlSchemaCollectionDatabase ) | |
! ADP . IsEmpty ( p . XmlSchemaCollectionOwningSchema ) | | ! ADP . IsEmpty ( p . XmlSchemaCollectionName ) ) {
flags | = 32 ; // v2.0 everything
}
Type [ ] ctorParams ;
object [ ] ctorValues ;
switch ( flags ) {
case 0 : // ParameterName
case 1 : // SqlDbType
ctorParams = new Type [ ] { typeof ( string ) , typeof ( SqlDbType ) } ;
ctorValues = new object [ ] { p . ParameterName , p . SqlDbType } ;
break ;
case 2 : // Size
case 3 : // Size, SqlDbType
ctorParams = new Type [ ] { typeof ( string ) , typeof ( SqlDbType ) , typeof ( int ) } ;
ctorValues = new object [ ] { p . ParameterName , p . SqlDbType , 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 ( SqlDbType ) , typeof ( int ) , typeof ( string ) } ;
ctorValues = new object [ ] { p . ParameterName , p . SqlDbType , 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 ( SqlDbType ) , typeof ( int ) , typeof ( ParameterDirection ) ,
typeof ( bool ) , typeof ( byte ) , typeof ( byte ) ,
typeof ( string ) , typeof ( DataRowVersion ) ,
typeof ( object ) } ;
ctorValues = new object [ ] {
p . ParameterName , p . SqlDbType , 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 ( SqlDbType ) , typeof ( int ) , typeof ( ParameterDirection ) ,
typeof ( byte ) , typeof ( byte ) ,
typeof ( string ) , typeof ( DataRowVersion ) , typeof ( bool ) ,
typeof ( object ) ,
typeof ( string ) , typeof ( string ) ,
typeof ( string ) } ;
ctorValues = new object [ ] {
p . ParameterName , p . SqlDbType , p . Size , p . Direction ,
p . PrecisionInternal , p . ScaleInternal ,
p . SourceColumn , p . SourceVersion , p . SourceColumnNullMapping ,
p . Value ,
p . XmlSchemaCollectionDatabase , p . XmlSchemaCollectionOwningSchema ,
p . XmlSchemaCollectionName } ;
}
break ;
}
System . Reflection . ConstructorInfo ctor = typeof ( SqlParameter ) . GetConstructor ( ctorParams ) ;
return new System . ComponentModel . Design . Serialization . InstanceDescriptor ( ctor , ctorValues ) ;
}
}
}
}