1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
//------------------------------------------------------------------------------
// <copyright file="TdsRecordBufferSetter.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>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using Microsoft.SqlServer.Server;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Data.SqlTypes;
// TdsRecordBufferSetter handles writing a structured value out to a TDS stream
internal class TdsRecordBufferSetter : SmiRecordBuffer {
#region Fields (private)
private TdsValueSetter[] _fieldSetters; // setters for individual fields
private TdsParserStateObject _stateObj; // target to write to
private SmiMetaData _metaData; // metadata describing value
#if DEBUG
private const int ReadyForToken = -1; // must call new/end element next
private const int EndElementsCalled = -2; // already called EndElements, can only call Close
private const int Closed = -3; // closed (zombied)
private int _currentField; // validate that caller sets columns in correct order.
#endif
#endregion
#region Exposed Construct and control methods/properties
internal TdsRecordBufferSetter(TdsParserStateObject stateObj, SmiMetaData md) {
Debug.Assert(SqlDbType.Structured == md.SqlDbType, "Unsupported SqlDbType: " + md.SqlDbType);
_fieldSetters = new TdsValueSetter[md.FieldMetaData.Count];
for(int i=0; i<md.FieldMetaData.Count; i++) {
_fieldSetters[i] = new TdsValueSetter(stateObj, md.FieldMetaData[i]);
}
_stateObj = stateObj;
_metaData = md;
#if DEBUG
_currentField = ReadyForToken;
#endif
}
// TdsRecordBufferSetter supports Setting only
internal override bool CanGet {
get {
return false;
}
}
internal override bool CanSet {
get {
return true;
}
}
// Close method does nothing (value is sent to TDS), but we need to override to avoid throwing
// Also make sure protocol is followed (no calling setters after calling close).
public override void Close(SmiEventSink eventSink) {
#if DEBUG
_currentField = Closed;
#endif
}
#endregion
#region Setters
// Set value to null
// valid for all types
public override void SetDBNull(SmiEventSink sink, int ordinal) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetDBNull();
}
// valid for SqlDbType.Bit
public override void SetBoolean(SmiEventSink sink, int ordinal, Boolean value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetBoolean(value);
}
// valid for SqlDbType.TinyInt
public override void SetByte(SmiEventSink sink, int ordinal, Byte value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetByte(value);
}
// Semantics for SetBytes are to modify existing value, not overwrite
// Use in combination with SetLength to ensure overwriting when necessary
// valid for SqlDbTypes: Binary, VarBinary, Image, Udt, Xml
// (VarBinary assumed for variants)
public override int SetBytes(SmiEventSink sink, int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) {
CheckWritingToColumn(ordinal);
return _fieldSetters[ordinal].SetBytes(fieldOffset, buffer, bufferOffset, length);
}
public override void SetBytesLength(SmiEventSink sink, int ordinal, long length) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetBytesLength(length);
}
// Semantics for SetChars are to modify existing value, not overwrite
// Use in combination with SetLength to ensure overwriting when necessary
// valid for character types: Char, VarChar, Text, NChar, NVarChar, NText
// (NVarChar and global clr collation assumed for variants)
public override int SetChars(SmiEventSink sink, int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) {
CheckWritingToColumn(ordinal);
return _fieldSetters[ordinal].SetChars(fieldOffset, buffer, bufferOffset, length);
}
public override void SetCharsLength(SmiEventSink sink, int ordinal, long length) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetCharsLength(length);
}
// valid for character types: Char, VarChar, Text, NChar, NVarChar, NText
public override void SetString(SmiEventSink sink, int ordinal, string value, int offset, int length) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetString(value, offset, length);
}
// valid for SqlDbType.SmallInt
public override void SetInt16(SmiEventSink sink, int ordinal, Int16 value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetInt16(value);
}
// valid for SqlDbType.Int
public override void SetInt32(SmiEventSink sink, int ordinal, Int32 value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetInt32(value);
}
// valid for SqlDbType.BigInt, SqlDbType.Money, SqlDbType.SmallMoney
public override void SetInt64(SmiEventSink sink, int ordinal, Int64 value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetInt64(value);
}
// valid for SqlDbType.Real
public override void SetSingle(SmiEventSink sink, int ordinal, Single value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetSingle(value);
}
// valid for SqlDbType.Float
public override void SetDouble(SmiEventSink sink, int ordinal, Double value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetDouble(value);
}
// valid for SqlDbType.Numeric (uses SqlDecimal since Decimal cannot hold full range)
public override void SetSqlDecimal(SmiEventSink sink, int ordinal, SqlDecimal value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetSqlDecimal(value);
}
// valid for DateTime, SmallDateTime, Date, DateTime2
public override void SetDateTime(SmiEventSink sink, int ordinal, DateTime value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetDateTime(value);
}
// valid for UniqueIdentifier
public override void SetGuid(SmiEventSink sink, int ordinal, Guid value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetGuid(value);
}
// valid for SqlDbType.Time
public override void SetTimeSpan(SmiEventSink sink, int ordinal, TimeSpan value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetTimeSpan(value);
}
// valid for DateTimeOffset
public override void SetDateTimeOffset(SmiEventSink sink, int ordinal, DateTimeOffset value) {
CheckSettingColumn(ordinal);
_fieldSetters[ordinal].SetDateTimeOffset(value);
}
// valid for SqlDbType.Variant
public override void SetVariantMetaData(SmiEventSink sink, int ordinal, SmiMetaData metaData) {
CheckWritingToColumn(ordinal);
_fieldSetters[ordinal].SetVariantType(metaData);
}
// valid for multi-valued types
internal override void NewElement(SmiEventSink sink) {
#if DEBUG
SkipPossibleDefaultedColumns(ReadyForToken);
Debug.Assert(ReadyForToken==_currentField, "Not on first or last column!");
#endif
// For TVP types, write new-row token
Debug.Assert(_metaData.IsMultiValued, "Unsupported call for single-valued types");
_stateObj.WriteByte(TdsEnums.TVP_ROW_TOKEN);
#if DEBUG
_currentField = 0;
#endif
}
internal override void EndElements(SmiEventSink sink) {
#if DEBUG
SkipPossibleDefaultedColumns(ReadyForToken);
Debug.Assert(ReadyForToken==_currentField, "Not on first or last column!");
Debug.Assert(_metaData.IsMultiValued, "Unsupported call for single-valued types");
#endif
// For TVP types, write no-more-rows token
_stateObj.WriteByte(TdsEnums.TVP_END_TOKEN);
#if DEBUG
_currentField = EndElementsCalled;
#endif
}
#endregion
#region private methods
[Conditional("DEBUG")]
private void CheckWritingToColumn(int ordinal) {
#if DEBUG
Debug.Assert(0 <= ordinal, "TdsRecordBufferSetter.CheckWritingToColumn: Targeting invalid column: " + ordinal);
SkipPossibleDefaultedColumns(ordinal);
Debug.Assert(0 <= _currentField && _metaData.FieldMetaData.Count > _currentField, "_currentField out of range for setting a column:" + _currentField);
Debug.Assert(ordinal == _currentField, "Setter called out of order. Should be " + _currentField + ", but was " + ordinal);
// Must not write to field with a DefaultFieldsProperty set to true
Debug.Assert(!((SmiDefaultFieldsProperty)_metaData.ExtendedProperties[SmiPropertySelector.DefaultFields])[ordinal],
"Attempt to write to a default-valued field: " + ordinal);
#endif
}
// Handle logic of skipping default columns
[Conditional("DEBUG")]
private void SkipPossibleDefaultedColumns(int targetColumn) {
#if DEBUG
Debug.Assert(targetColumn < _metaData.FieldMetaData.Count && targetColumn >= ReadyForToken, "TdsRecordBufferSetter.SkipPossibleDefaultedColumns: Invalid target column: " + targetColumn);
// special setup for ReadyForToken as the target
if (targetColumn == ReadyForToken) {
if (ReadyForToken == _currentField) {
return;
}
// Handle readyfortoken by using count of columns in the loop.
targetColumn = _metaData.FieldMetaData.Count;
}
// Handle skipping default-valued fields
while (targetColumn > _currentField) {
// All intermediate fields must be default fields (i.e. have a "true" entry in SmiDefaultFieldsProperty
Debug.Assert(((SmiDefaultFieldsProperty)_metaData.ExtendedProperties[SmiPropertySelector.DefaultFields])[_currentField],
"Skipping a field that was not default: " + _currentField);
_currentField++;
}
if (_metaData.FieldMetaData.Count == _currentField) {
_currentField = ReadyForToken;
}
#endif
}
[Conditional("DEBUG")]
internal void CheckSettingColumn(int ordinal) {
#if DEBUG
// Make sure target column can be written to.
CheckWritingToColumn(ordinal);
_currentField++;
if (_metaData.FieldMetaData.Count == _currentField) {
_currentField = ReadyForToken;
}
#endif
}
#endregion
}
}
|