/// <summary> /// Writes the crypto metadata (as part of COLMETADATA token) for encrypted columns. /// </summary> /// <returns></returns> internal void WriteCryptoMetadata(_SqlMetaData md, TdsParserStateObject stateObj) { if (!_serverSupportsColumnEncryption || // TCE Feature supported !md.isEncrypted || // Column is not encrypted !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string return; } // Write the ordinal WriteShort (md.cipherMD.CekTableOrdinal, stateObj); // Write UserType and TYPEINFO WriteTceUserTypeAndTypeInfo(md.baseTI, stateObj); // Write Encryption Algo stateObj.WriteByte(md.cipherMD.CipherAlgorithmId); if (TdsEnums.CustomCipherAlgorithmId == md.cipherMD.CipherAlgorithmId) { // Write the algorithm name Debug.Assert (md.cipherMD.CipherAlgorithmName.Length < 256); stateObj.WriteByte((byte)md.cipherMD.CipherAlgorithmName.Length); WriteString(md.cipherMD.CipherAlgorithmName, stateObj); } // Write Encryption Algo Type stateObj.WriteByte(md.cipherMD.EncryptionType); // Write Normalization Version stateObj.WriteByte(md.cipherMD.NormalizationRuleVersion); }
// // Reverse function of GetTokenLength // private void WriteTokenLength(byte token, int length, TdsParserStateObject stateObj) { int tokenLength = 0; Debug.Assert(token != 0, "0 length token!"); // For Plp fields, this should only be used when writing to metadata header. // For actual data length, WriteDataLength should be used. // For Xml fields, there is no token length field. For MAX fields it is 0xffff. { if (TdsEnums.SQLUDT == token) { tokenLength = 8; } else if (token == TdsEnums.SQLXMLTYPE) { tokenLength = 8; } } if (tokenLength == 0) { switch (token & TdsEnums.SQLLenMask) { case TdsEnums.SQLFixedLen: Debug.Assert(length == 0x01 << ((token & 0x0c) >> 2), "length does not match encoded length in token"); tokenLength = 0; break; case TdsEnums.SQLZeroLen: tokenLength = 0; break; case TdsEnums.SQLVarLen: case TdsEnums.SQLVarCnt: if (0 != (token & 0x80)) tokenLength = 2; else if (0 == (token & 0x0c)) tokenLength = 4; else tokenLength = 1; break; default: Debug.Assert(false, "Unknown token length!"); break; } switch (tokenLength) { case 1: stateObj.WriteByte((byte)length); break; case 2: WriteShort(length, stateObj); break; case 4: WriteInt(length, stateObj); break; case 8: // In the metadata case we write 0xffff for partial length prefixed types. // For actual data length preceding data, WriteDataLength should be used. WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj); break; } // end switch } }
// For MAX types, this method can only write everything in one big chunk. If multiple // chunk writes needed, please use WritePlpBytes/WritePlpChars private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj, int paramSize, bool isDataFeed) { Debug.Assert((null != value) && (DBNull.Value != value), "unexpected missing or empty object"); // parameters are always sent over as BIG or N types switch (type.NullableType) { case TdsEnums.SQLFLTN: if (type.FixedLength == 4) WriteFloat((Single)value, stateObj); else { Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!"); WriteDouble((Double)value, stateObj); } break; case TdsEnums.SQLBIGBINARY: case TdsEnums.SQLBIGVARBINARY: case TdsEnums.SQLIMAGE: case TdsEnums.SQLUDT: { // An array should be in the object Debug.Assert(isDataFeed || value is byte[], "Value should be an array of bytes"); Debug.Assert(!isDataFeed || value is StreamDataFeed, "Value should be a stream"); if (isDataFeed) { Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!"); return NullIfCompletedWriteTask(WriteStreamFeed((StreamDataFeed)value, stateObj, paramSize)); } else { if (type.IsPlp) { WriteInt(actualLength, stateObj); // chunk length } return stateObj.WriteByteArray((byte[])value, actualLength, offset, canAccumulate: false); } } case TdsEnums.SQLUNIQUEID: { System.Guid guid = (System.Guid)value; byte[] b = guid.ToByteArray(); Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object"); stateObj.WriteByteArray(b, actualLength, 0); break; } case TdsEnums.SQLBITN: { Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type"); if ((bool)value == true) stateObj.WriteByte(1); else stateObj.WriteByte(0); break; } case TdsEnums.SQLINTN: if (type.FixedLength == 1) stateObj.WriteByte((byte)value); else if (type.FixedLength == 2) WriteShort((Int16)value, stateObj); else if (type.FixedLength == 4) WriteInt((Int32)value, stateObj); else { Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type: " + type.FixedLength.ToString(CultureInfo.InvariantCulture)); WriteLong((Int64)value, stateObj); } break; case TdsEnums.SQLBIGCHAR: case TdsEnums.SQLBIGVARCHAR: case TdsEnums.SQLTEXT: { Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader"); Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string"); if (isDataFeed) { Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!"); TextDataFeed tdf = value as TextDataFeed; if (tdf == null) { return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed)value, stateObj, needBom: true, encoding: _defaultEncoding, size: paramSize)); } else { return NullIfCompletedWriteTask(WriteTextFeed(tdf, _defaultEncoding, false, stateObj, paramSize)); } } else { if (type.IsPlp) { WriteInt(encodingByteSize, stateObj); // chunk length } if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value return stateObj.WriteByteArray((byte[])value, actualLength, 0, canAccumulate: false); } else { return WriteEncodingChar((string)value, actualLength, offset, _defaultEncoding, stateObj, canAccumulate: false); } } } case TdsEnums.SQLNCHAR: case TdsEnums.SQLNVARCHAR: case TdsEnums.SQLNTEXT: case TdsEnums.SQLXMLTYPE: { Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader"); Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string"); if (isDataFeed) { Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!"); TextDataFeed tdf = value as TextDataFeed; if (tdf == null) { return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed)value, stateObj, IsBOMNeeded(type, value), Encoding.Unicode, paramSize)); } else { return NullIfCompletedWriteTask(WriteTextFeed(tdf, null, IsBOMNeeded(type, value), stateObj, paramSize)); } } else { if (type.IsPlp) { if (IsBOMNeeded(type, value)) { WriteInt(actualLength + 2, stateObj); // chunk length WriteShort(TdsEnums.XMLUNICODEBOM, stateObj); } else { WriteInt(actualLength, stateObj); // chunk length } } if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value return stateObj.WriteByteArray((byte[])value, actualLength, 0, canAccumulate: false); } else { // convert to cchars instead of cbytes actualLength >>= 1; return WriteString((string)value, actualLength, offset, stateObj, canAccumulate: false); } } } case TdsEnums.SQLNUMERICN: Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes"); WriteDecimal((Decimal)value, stateObj); break; case TdsEnums.SQLDATETIMN: Debug.Assert(type.FixedLength <= 0xff, "Invalid Fixed Length"); TdsDateTime dt = MetaType.FromDateTime((DateTime)value, (byte)type.FixedLength); if (type.FixedLength == 4) { if (0 > dt.days || dt.days > UInt16.MaxValue) throw SQL.SmallDateTimeOverflow(MetaType.ToDateTime(dt.days, dt.time, 4).ToString(CultureInfo.InvariantCulture)); WriteShort(dt.days, stateObj); WriteShort(dt.time, stateObj); } else { WriteInt(dt.days, stateObj); WriteInt(dt.time, stateObj); } break; case TdsEnums.SQLMONEYN: { WriteCurrency((Decimal)value, type.FixedLength, stateObj); break; } case TdsEnums.SQLDATE: { WriteDate((DateTime)value, stateObj); break; } case TdsEnums.SQLTIME: if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) { throw SQL.TimeScaleValueOutOfRange(scale); } WriteTime((TimeSpan)value, scale, actualLength, stateObj); break; case TdsEnums.SQLDATETIME2: if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) { throw SQL.TimeScaleValueOutOfRange(scale); } WriteDateTime2((DateTime)value, scale, actualLength, stateObj); break; case TdsEnums.SQLDATETIMEOFFSET: WriteDateTimeOffset((DateTimeOffset)value, scale, actualLength, stateObj); break; default: Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); break; } // switch // return point for accumualated writes, note: non-accumulated writes returned from their case statements return null; // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture)); }
private void WriteTvpOrderUnique(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { // TVP_ORDER_UNIQUE token (uniqueness and sort order) // Merge order and unique keys into a single token stream MSS.SmiOrderProperty orderProperty = (MSS.SmiOrderProperty)metaData.ExtendedProperties[MSS.SmiPropertySelector.SortOrder]; MSS.SmiUniqueKeyProperty uniqueKeyProperty = (MSS.SmiUniqueKeyProperty)metaData.ExtendedProperties[MSS.SmiPropertySelector.UniqueKey]; // Build list from List<TdsOrderUnique> columnList = new List<TdsOrderUnique>(metaData.FieldMetaData.Count); for (int i = 0; i < metaData.FieldMetaData.Count; i++) { // Add appropriate SortOrder flag byte flags = 0; MSS.SmiOrderProperty.SmiColumnOrder columnOrder = orderProperty[i]; if (SortOrder.Ascending == columnOrder.Order) { flags = TdsEnums.TVP_ORDERASC_FLAG; } else if (SortOrder.Descending == columnOrder.Order) { flags = TdsEnums.TVP_ORDERDESC_FLAG; } // Add unique key flage if appropriate if (uniqueKeyProperty[i]) { flags |= TdsEnums.TVP_UNIQUE_FLAG; } // Remember this column if any flags were set if (0 != flags) { columnList.Add(new TdsOrderUnique(checked((short)(i + 1)), flags)); } } // Write flagged columns to wire... if (0 < columnList.Count) { stateObj.WriteByte(TdsEnums.TVP_ORDER_UNIQUE_TOKEN); WriteShort(columnList.Count, stateObj); foreach (TdsOrderUnique column in columnList) { WriteShort(column.ColumnOrdinal, stateObj); stateObj.WriteByte(column.Flags); } } }
internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int count, TdsParserStateObject stateObj) { if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) { throw ADP.ClosedConnectionError(); } stateObj.WriteByte(TdsEnums.SQLCOLMETADATA); WriteShort(count, stateObj); for (int i = 0; i < metadataCollection.Length; i++) { if (metadataCollection[i] != null) { _SqlMetaData md = metadataCollection[i]; // read user type - 4 bytes Yukon, 2 backwards WriteInt(0x0, stateObj); UInt16 flags; flags = (UInt16)(md.updatability << 2); flags |= (UInt16)(md.isNullable ? (UInt16)TdsEnums.Nullable : (UInt16)0); flags |= (UInt16)(md.isIdentity ? (UInt16)TdsEnums.Identity : (UInt16)0); WriteShort(flags, stateObj); // write the flags switch (md.type) { case SqlDbType.Decimal: stateObj.WriteByte(md.tdsType); WriteTokenLength(md.tdsType, md.length, stateObj); stateObj.WriteByte(md.precision); stateObj.WriteByte(md.scale); break; case SqlDbType.Xml: stateObj.WriteByteArray(s_xmlMetadataSubstituteSequence, s_xmlMetadataSubstituteSequence.Length, 0); break; case SqlDbType.Udt: throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); case SqlDbType.Date: stateObj.WriteByte(md.tdsType); break; case SqlDbType.Time: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: stateObj.WriteByte(md.tdsType); stateObj.WriteByte(md.scale); break; default: stateObj.WriteByte(md.tdsType); WriteTokenLength(md.tdsType, md.length, stateObj); if (md.metaType.IsCharType) { WriteUnsignedInt(md.collation.info, stateObj); stateObj.WriteByte(md.collation.sortId); } break; } if (md.metaType.IsLong && !md.metaType.IsPlp) { WriteShort(md.tableName.Length, stateObj); WriteString(md.tableName, stateObj); } stateObj.WriteByte((byte)md.column.Length); WriteString(md.column, stateObj); } } // end for loop }
private void WriteParameterName(string parameterName, TdsParserStateObject stateObj) { // paramLen // paramName if (!ADP.IsEmpty(parameterName)) { Debug.Assert(parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!"); int tempLen = parameterName.Length & 0xff; stateObj.WriteByte((byte)tempLen); WriteString(parameterName, tempLen, 0, stateObj); } else { stateObj.WriteByte(0); } }
// Write a TypeInfo stream // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR, SQLNVARCHAR, and SQLBIGVARBINARY private void WriteSmiTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { switch (metaData.SqlDbType) { case SqlDbType.BigInt: stateObj.WriteByte(TdsEnums.SQLINTN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.Binary: stateObj.WriteByte(TdsEnums.SQLBIGBINARY); WriteUnsignedShort(checked((ushort)metaData.MaxLength), stateObj); break; case SqlDbType.Bit: stateObj.WriteByte(TdsEnums.SQLBITN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.Char: stateObj.WriteByte(TdsEnums.SQLBIGCHAR); WriteUnsignedShort(checked((ushort)(metaData.MaxLength)), stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); stateObj.WriteByte(_defaultCollation.sortId); break; case SqlDbType.DateTime: stateObj.WriteByte(TdsEnums.SQLDATETIMN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.Decimal: stateObj.WriteByte(TdsEnums.SQLNUMERICN); stateObj.WriteByte(checked((byte)MetaType.MetaDecimal.FixedLength)); // SmiMetaData's length and actual wire format's length are different stateObj.WriteByte(0 == metaData.Precision ? (byte)1 : metaData.Precision); stateObj.WriteByte(metaData.Scale); break; case SqlDbType.Float: stateObj.WriteByte(TdsEnums.SQLFLTN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.Image: stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY); WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); break; case SqlDbType.Int: stateObj.WriteByte(TdsEnums.SQLINTN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.Money: stateObj.WriteByte(TdsEnums.SQLMONEYN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.NChar: stateObj.WriteByte(TdsEnums.SQLNCHAR); WriteUnsignedShort(checked((ushort)(metaData.MaxLength * 2)), stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); stateObj.WriteByte(_defaultCollation.sortId); break; case SqlDbType.NText: stateObj.WriteByte(TdsEnums.SQLNVARCHAR); WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); stateObj.WriteByte(_defaultCollation.sortId); break; case SqlDbType.NVarChar: stateObj.WriteByte(TdsEnums.SQLNVARCHAR); if (MSS.SmiMetaData.UnlimitedMaxLengthIndicator == metaData.MaxLength) { WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); } else { WriteUnsignedShort(checked((ushort)(metaData.MaxLength * 2)), stateObj); } WriteUnsignedInt(_defaultCollation.info, stateObj); stateObj.WriteByte(_defaultCollation.sortId); break; case SqlDbType.Real: stateObj.WriteByte(TdsEnums.SQLFLTN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.UniqueIdentifier: stateObj.WriteByte(TdsEnums.SQLUNIQUEID); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.SmallDateTime: stateObj.WriteByte(TdsEnums.SQLDATETIMN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.SmallInt: stateObj.WriteByte(TdsEnums.SQLINTN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.SmallMoney: stateObj.WriteByte(TdsEnums.SQLMONEYN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.Text: stateObj.WriteByte(TdsEnums.SQLBIGVARCHAR); WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); stateObj.WriteByte(_defaultCollation.sortId); break; case SqlDbType.Timestamp: stateObj.WriteByte(TdsEnums.SQLBIGBINARY); WriteShort(checked((int)metaData.MaxLength), stateObj); break; case SqlDbType.TinyInt: stateObj.WriteByte(TdsEnums.SQLINTN); stateObj.WriteByte(checked((byte)metaData.MaxLength)); break; case SqlDbType.VarBinary: stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY); WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj); break; case SqlDbType.VarChar: stateObj.WriteByte(TdsEnums.SQLBIGVARCHAR); WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); stateObj.WriteByte(_defaultCollation.sortId); break; case SqlDbType.Variant: stateObj.WriteByte(TdsEnums.SQLVARIANT); WriteInt(checked((int)metaData.MaxLength), stateObj); break; case SqlDbType.Xml: stateObj.WriteByte(TdsEnums.SQLXMLTYPE); // Is there a schema if (ADP.IsEmpty(metaData.TypeSpecificNamePart1) && ADP.IsEmpty(metaData.TypeSpecificNamePart2) && ADP.IsEmpty(metaData.TypeSpecificNamePart3)) { stateObj.WriteByte(0); // schema not present } else { stateObj.WriteByte(1); // schema present WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj); WriteIdentifierWithShortLength(metaData.TypeSpecificNamePart3, stateObj); } break; case SqlDbType.Udt: stateObj.WriteByte(TdsEnums.SQLUDT); WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj); WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj); break; case SqlDbType.Structured: if (metaData.IsMultiValued) { WriteTvpTypeInfo(metaData, stateObj); } else { Debug.Assert(false, "SUDTs not yet supported."); } break; case SqlDbType.Date: stateObj.WriteByte(TdsEnums.SQLDATE); break; case SqlDbType.Time: stateObj.WriteByte(TdsEnums.SQLTIME); stateObj.WriteByte(metaData.Scale); break; case SqlDbType.DateTime2: stateObj.WriteByte(TdsEnums.SQLDATETIME2); stateObj.WriteByte(metaData.Scale); break; case SqlDbType.DateTimeOffset: stateObj.WriteByte(TdsEnums.SQLDATETIMEOFFSET); stateObj.WriteByte(metaData.Scale); break; default: Debug.Assert(false, "Unknown SqlDbType should have been caught earlier!"); break; } }
// // Takes a long and writes part of it // internal void WritePartialLong(long v, int length, TdsParserStateObject stateObj) { Debug.Assert(length <= 8, "Length specified is longer than the size of a long"); Debug.Assert(length >= 0, "Length should not be negative"); if ((stateObj._outBytesUsed + length) > stateObj._outBuff.Length) { // if all of the long doesn't fit into the buffer for (int shiftValue = 0; shiftValue < length * 8; shiftValue += 8) { stateObj.WriteByte((byte)((v >> shiftValue) & 0xff)); } } else { // all of the long fits into the buffer for (int index = 0; index < length; index++) { stateObj._outBuff[stateObj._outBytesUsed + index] = (byte)((v >> (index * 8)) & 0xff); } stateObj._outBytesUsed += length; } }
// // Translates a com+ object -> SqlVariant // when the type is ambiguous, we always convert to the bigger type // note that we also write out the maxlen and actuallen members (4 bytes each) // in addition to the SQLVariant structure // internal Task WriteSqlVariantValue(object value, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true) { // handle null values if (ADP.IsNull(value)) { WriteInt(TdsEnums.FIXEDNULL, stateObj); //maxlen WriteInt(TdsEnums.FIXEDNULL, stateObj); //actuallen return null; } MetaType mt = MetaType.GetMetaTypeFromValue(value); // Special case data type correction for SqlMoney inside a SqlVariant. if ((TdsEnums.SQLNUMERICN == mt.TDSType) && (8 == length)) { // The caller will coerce all SqlTypes to native CLR types, which means SqlMoney will // coerce to decimal/SQLNUMERICN (via SqlMoney.Value call). In the case where the original // value was SqlMoney the caller will also pass in the metadata length for the SqlMoney type // which is 8 bytes. To honor the intent of the caller here we coerce this special case // input back to SqlMoney from decimal/SQLNUMERICN. mt = MetaType.GetMetaTypeFromValue(new SqlMoney((decimal)value)); } if (mt.IsAnsiType) { length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding); } // max and actual len are equal to // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes) WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // maxLen WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // actualLen // write the SQLVariant header (type and cbPropBytes) stateObj.WriteByte(mt.TDSType); stateObj.WriteByte(mt.PropBytes); // now write the actual PropBytes and data switch (mt.TDSType) { case TdsEnums.SQLFLT4: WriteFloat((Single)value, stateObj); break; case TdsEnums.SQLFLT8: WriteDouble((Double)value, stateObj); break; case TdsEnums.SQLINT8: WriteLong((Int64)value, stateObj); break; case TdsEnums.SQLINT4: WriteInt((Int32)value, stateObj); break; case TdsEnums.SQLINT2: WriteShort((Int16)value, stateObj); break; case TdsEnums.SQLINT1: stateObj.WriteByte((byte)value); break; case TdsEnums.SQLBIT: if ((bool)value == true) stateObj.WriteByte(1); else stateObj.WriteByte(0); break; case TdsEnums.SQLBIGVARBINARY: { byte[] b = (byte[])value; WriteShort(length, stateObj); // propbytes: varlen return stateObj.WriteByteArray(b, length, offset, canAccumulate); } case TdsEnums.SQLBIGVARCHAR: { string s = (string)value; WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId WriteShort(length, stateObj); // propbyte: varlen return WriteEncodingChar(s, _defaultEncoding, stateObj, canAccumulate); } case TdsEnums.SQLUNIQUEID: { System.Guid guid = (System.Guid)value; byte[] b = guid.ToByteArray(); Debug.Assert((length == b.Length) && (length == 16), "Invalid length for guid type in com+ object"); stateObj.WriteByteArray(b, length, 0); break; } case TdsEnums.SQLNVARCHAR: { string s = (string)value; WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId WriteShort(length, stateObj); // propbyte: varlen // string takes cchar, not cbyte so convert length >>= 1; return WriteString(s, length, offset, stateObj, canAccumulate); } case TdsEnums.SQLDATETIME: { TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8); WriteInt(dt.days, stateObj); WriteInt(dt.time, stateObj); break; } case TdsEnums.SQLMONEY: { WriteCurrency((Decimal)value, 8, stateObj); break; } case TdsEnums.SQLNUMERICN: { stateObj.WriteByte(mt.Precision); //propbytes: precision stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale WriteDecimal((Decimal)value, stateObj); break; } case TdsEnums.SQLTIME: stateObj.WriteByte(mt.Scale); //propbytes: scale WriteTime((TimeSpan)value, mt.Scale, length, stateObj); break; case TdsEnums.SQLDATETIMEOFFSET: stateObj.WriteByte(mt.Scale); //propbytes: scale WriteDateTimeOffset((DateTimeOffset)value, mt.Scale, length, stateObj); break; default: Debug.Assert(false, "unknown tds type for sqlvariant!"); break; } // switch // return point for accumulated writes, note: non-accumulated writes returned from their case statements return null; }
// // Takes an int and writes it as an int. // internal void WriteInt(int v, TdsParserStateObject stateObj) { if ((stateObj._outBytesUsed + 4) > stateObj._outBuff.Length) { // if all of the int doesn't fit into the buffer for (int shiftValue = 0; shiftValue < sizeof(int) * 8; shiftValue += 8) { stateObj.WriteByte((byte)((v >> shiftValue) & 0xff)); } } else { // all of the int fits into the buffer // NOTE: We don't use a loop here for performance stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff); stateObj._outBytesUsed += 4; } }
// // Takes a long and writes it as a long. // internal void WriteLong(long v, TdsParserStateObject stateObj) { if ((stateObj._outBytesUsed + 8) > stateObj._outBuff.Length) { // if all of the long doesn't fit into the buffer for (int shiftValue = 0; shiftValue < sizeof(long) * 8; shiftValue += 8) { stateObj.WriteByte((byte)((v >> shiftValue) & 0xff)); } } else { // all of the long fits into the buffer // NOTE: We don't use a loop here for performance stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 4] = (byte)((v >> 32) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 5] = (byte)((v >> 40) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 6] = (byte)((v >> 48) & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 7] = (byte)((v >> 56) & 0xff); stateObj._outBytesUsed += 8; } }
// // Takes a 16 bit short and writes it. // internal void WriteShort(int v, TdsParserStateObject stateObj) { if ((stateObj._outBytesUsed + 2) > stateObj._outBuff.Length) { // if all of the short doesn't fit into the buffer stateObj.WriteByte((byte)(v & 0xff)); stateObj.WriteByte((byte)((v >> 8) & 0xff)); } else { // all of the short fits into the buffer stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff); stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff); stateObj._outBytesUsed += 2; } }
internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) { // sign if (d.IsPositive) stateObj.WriteByte(1); else stateObj.WriteByte(0); uint data1, data2, data3, data4; SqlTypeWorkarounds.SqlDecimalExtractData(d, out data1, out data2, out data3, out data4); WriteUnsignedInt(data1, stateObj); WriteUnsignedInt(data2, stateObj); WriteUnsignedInt(data3, stateObj); WriteUnsignedInt(data4, stateObj); }
internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int count, TdsParserStateObject stateObj) { if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) { throw ADP.ClosedConnectionError(); } stateObj.WriteByte(TdsEnums.SQLCOLMETADATA); WriteShort(count, stateObj); // Write CEK table - 0 count WriteCekTable(metadataCollection, stateObj); for (int i = 0; i < metadataCollection.Length; i++) { if (metadataCollection[i] != null) { _SqlMetaData md = metadataCollection[i]; // read user type - 4 bytes Yukon, 2 backwards if (IsYukonOrNewer) { WriteInt(0x0, stateObj); } else { WriteShort(0x0000, stateObj); } // Write the flags UInt16 flags; flags = (UInt16)(md.updatability << 2); flags |= (UInt16)(md.isNullable ? (UInt16)TdsEnums.Nullable : (UInt16)0); flags |= (UInt16)(md.isIdentity ? (UInt16)TdsEnums.Identity : (UInt16)0); // Write the next byte of flags if (_serverSupportsColumnEncryption) { // TCE Supported if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options flags |= (UInt16)(md.isEncrypted ? (UInt16)(TdsEnums.IsEncrypted << 8) : (UInt16)0); } } WriteShort(flags, stateObj);// write the flags // todo: // for xml WriteTokenLength results in a no-op // discuss this with blaine ... // ([....]) xml datatype does not have token length in its metadata. So it should be a noop. switch (md.type) { case SqlDbType.Decimal: stateObj.WriteByte(md.tdsType); WriteTokenLength(md.tdsType, md.length, stateObj); stateObj.WriteByte(md.precision); stateObj.WriteByte(md.scale); break; case SqlDbType.Xml: // stateObj.WriteByteArray(s_xmlMetadataSubstituteSequence, s_xmlMetadataSubstituteSequence.Length, 0); break; case SqlDbType.Udt: stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY); WriteTokenLength(TdsEnums.SQLBIGVARBINARY, md.length, stateObj); break; case SqlDbType.Date: stateObj.WriteByte(md.tdsType); break; case SqlDbType.Time: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: stateObj.WriteByte(md.tdsType); stateObj.WriteByte(md.scale); break; default: stateObj.WriteByte(md.tdsType); WriteTokenLength(md.tdsType, md.length, stateObj); if (md.metaType.IsCharType && _isShiloh) { WriteUnsignedInt(md.collation.info, stateObj); stateObj.WriteByte(md.collation.sortId); } break; } if (md.metaType.IsLong && !md.metaType.IsPlp) { WriteShort(md.tableName.Length, stateObj); WriteString(md.tableName, stateObj); } WriteCryptoMetadata(md, stateObj); stateObj.WriteByte((byte)md.column.Length); WriteString(md.column, stateObj); } } // end for loop }
internal SqlDataReader TdsExecuteTransactionManagerRequest( byte[] buffer, TdsEnums.TransactionManagerRequestType request, string transactionName, TdsEnums.TransactionManagerIsolationLevel isoLevel, int timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj ) { Debug.Assert(this == stateObj.Parser, "different parsers"); if (TdsParserState.Broken == State || TdsParserState.Closed == State) { return null; } // Promote, Commit and Rollback requests for // delegated transactions often happen while there is an open result // set, so we need to handle them by using a different MARS session, // otherwise we'll write on the physical state objects while someone // else is using it. When we don't have MARS enabled, we need to // lock the physical state object to syncronize it's use at least // until we increment the open results count. Once it's been // incremented the delegated transaction requests will fail, so they // won't stomp on anything. Debug.Assert(!_connHandler.ThreadHasParserLockForClose || _connHandler._parserLock.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken"); bool callerHasConnectionLock = _connHandler.ThreadHasParserLockForClose; // If the thread already claims to have the parser lock, then we will let the caller handle releasing it if (!callerHasConnectionLock) { _connHandler._parserLock.Wait(canReleaseFromAnyThread: false); _connHandler.ThreadHasParserLockForClose = true; } // Capture _asyncWrite (after taking lock) to restore it afterwards bool hadAsyncWrites = _asyncWrite; try { // Temprarily disable async writes _asyncWrite = false; stateObj._outputMessageType = TdsEnums.MT_TRANS; // set message type stateObj.SetTimeoutSeconds(timeout); stateObj.SniContext = SniContext.Snix_Execute; const int marsHeaderSize = 18; // 4 + 2 + 8 + 4 const int totalHeaderLength = 22; // 4 + 4 + 2 + 8 + 4 Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length"); // Write total header length WriteInt(totalHeaderLength, stateObj); // Write mars header length WriteInt(marsHeaderSize, stateObj); WriteMarsHeaderData(stateObj, _currentTransaction); WriteShort((short)request, stateObj); // write TransactionManager Request type bool returnReader = false; switch (request) { case TdsEnums.TransactionManagerRequestType.Begin: Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!"); // Only assign the passed in transaction if it is not equal to the current transaction. // And, if it is not equal, the current actually should be null. Anything else // is a unexpected state. The concern here is mainly for the mixed use of // T-SQL and API transactions. // Expected states: // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction // passed in on BeginTransaction API call. // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction // passed in but equivalent to _currentTransaction. // #1 will occur on standard BeginTransactionAPI call. #2 should only occur if // t-sql transaction started followed by a call to SqlConnection.BeginTransaction. // Any other state is unknown. if (_currentTransaction != transaction) { Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point"); PendingTransaction = transaction; } stateObj.WriteByte((byte)isoLevel); stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string). WriteString(transactionName, stateObj); break; case TdsEnums.TransactionManagerRequestType.Commit: Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit"); stateObj.WriteByte((byte)0); // No xact name stateObj.WriteByte(0); // No flags Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); // WriteByte((byte) 0, stateObj); // IsolationLevel // WriteByte((byte) 0, stateObj); // No begin xact name break; case TdsEnums.TransactionManagerRequestType.Rollback: stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string). WriteString(transactionName, stateObj); stateObj.WriteByte(0); // No flags Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); // WriteByte((byte) 0, stateObj); // IsolationLevel // WriteByte((byte) 0, stateObj); // No begin xact name break; case TdsEnums.TransactionManagerRequestType.Save: stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string). WriteString(transactionName, stateObj); break; default: Debug.Assert(false, "Unexpected TransactionManagerRequest"); break; } Task writeTask = stateObj.WritePacket(TdsEnums.HARDFLUSH); Debug.Assert(writeTask == null, "Writes should not pend when writing sync"); stateObj._pendingData = true; stateObj._messageStatus = 0; SqlDataReader dtcReader = null; stateObj.SniContext = SniContext.Snix_Read; if (returnReader) { dtcReader = new SqlDataReader(null, CommandBehavior.Default); Debug.Assert(this == stateObj.Parser, "different parser"); #if DEBUG // Remove the current owner of stateObj - otherwise we will hit asserts stateObj.Owner = null; #endif dtcReader.Bind(stateObj); // force consumption of metadata _SqlMetaDataSet metaData = dtcReader.MetaData; } else { Run(RunBehavior.UntilDone, null, null, null, stateObj); } return dtcReader; } catch (Exception e) { if (!ADP.IsCatchableExceptionType(e)) { throw; } FailureCleanup(stateObj, e); throw; } finally { // SQLHotfix 50000518 // make sure we don't leave temporary fields set when leaving this function _pendingTransaction = null; _asyncWrite = hadAsyncWrites; if (!callerHasConnectionLock) { _connHandler.ThreadHasParserLockForClose = false; _connHandler._parserLock.Release(); } } }
// todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider // combining these tow methods. // // Translates a com+ object -> SqlVariant // when the type is ambiguous, we always convert to the bigger type // note that we also write out the maxlen and actuallen members (4 bytes each) // in addition to the SQLVariant structure // // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value. // Therefore the sql_variant value must not include the MaxLength. This is the major difference // between this method and WriteSqlVariantValue above. // internal Task WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj, bool canAccumulate = true) { // handle null values if ((null == value) || (DBNull.Value == value)) { WriteInt(TdsEnums.FIXEDNULL, stateObj); return null; } MetaType metatype = MetaType.GetMetaTypeFromValue(value); int length = 0; if (metatype.IsAnsiType) { length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding); } switch (metatype.TDSType) { case TdsEnums.SQLFLT4: WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj); WriteFloat((Single)value, stateObj); break; case TdsEnums.SQLFLT8: WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); WriteDouble((Double)value, stateObj); break; case TdsEnums.SQLINT8: WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); WriteLong((Int64)value, stateObj); break; case TdsEnums.SQLINT4: WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj); WriteInt((Int32)value, stateObj); break; case TdsEnums.SQLINT2: WriteSqlVariantHeader(4, metatype.TDSType, metatype.PropBytes, stateObj); WriteShort((Int16)value, stateObj); break; case TdsEnums.SQLINT1: WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj); stateObj.WriteByte((byte)value); break; case TdsEnums.SQLBIT: WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj); if ((bool)value == true) stateObj.WriteByte(1); else stateObj.WriteByte(0); break; case TdsEnums.SQLBIGVARBINARY: { byte[] b = (byte[])value; length = b.Length; WriteSqlVariantHeader(4 + length, metatype.TDSType, metatype.PropBytes, stateObj); WriteShort(length, stateObj); // propbytes: varlen return stateObj.WriteByteArray(b, length, 0, canAccumulate); } case TdsEnums.SQLBIGVARCHAR: { string s = (string)value; length = s.Length; WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId WriteShort(length, stateObj); return WriteEncodingChar(s, _defaultEncoding, stateObj, canAccumulate); } case TdsEnums.SQLUNIQUEID: { System.Guid guid = (System.Guid)value; byte[] b = guid.ToByteArray(); length = b.Length; Debug.Assert(length == 16, "Invalid length for guid type in com+ object"); WriteSqlVariantHeader(18, metatype.TDSType, metatype.PropBytes, stateObj); stateObj.WriteByteArray(b, length, 0); break; } case TdsEnums.SQLNVARCHAR: { string s = (string)value; length = s.Length * 2; WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj); WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId WriteShort(length, stateObj); // propbyte: varlen // string takes cchar, not cbyte so convert length >>= 1; return WriteString(s, length, 0, stateObj, canAccumulate); } case TdsEnums.SQLDATETIME: { TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8); WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); WriteInt(dt.days, stateObj); WriteInt(dt.time, stateObj); break; } case TdsEnums.SQLMONEY: { WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); WriteCurrency((Decimal)value, 8, stateObj); break; } case TdsEnums.SQLNUMERICN: { WriteSqlVariantHeader(21, metatype.TDSType, metatype.PropBytes, stateObj); stateObj.WriteByte(metatype.Precision); //propbytes: precision stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale WriteDecimal((Decimal)value, stateObj); break; } case TdsEnums.SQLTIME: WriteSqlVariantHeader(8, metatype.TDSType, metatype.PropBytes, stateObj); stateObj.WriteByte(metatype.Scale); //propbytes: scale WriteTime((TimeSpan)value, metatype.Scale, 5, stateObj); break; case TdsEnums.SQLDATETIMEOFFSET: WriteSqlVariantHeader(13, metatype.TDSType, metatype.PropBytes, stateObj); stateObj.WriteByte(metatype.Scale); //propbytes: scale WriteDateTimeOffset((DateTimeOffset)value, metatype.Scale, 10, stateObj); break; default: Debug.Assert(false, "unknown tds type for sqlvariant!"); break; } // switch // return point for accumualated writes, note: non-accumulated writes returned from their case statements return null; }
internal Task TdsExecuteRPC(_SqlRPC[] rpcArray, int timeout, bool inSchema, TdsParserStateObject stateObj, bool isCommandProc, bool sync = true, TaskCompletionSource<object> completion = null, int startRpc = 0, int startParam = 0) { bool firstCall = (completion == null); bool releaseConnectionLock = false; Debug.Assert(!firstCall || startRpc == 0, "startRpc is not 0 on first call"); Debug.Assert(!firstCall || startParam == 0, "startParam is not 0 on first call"); Debug.Assert(!firstCall || !_connHandler.ThreadHasParserLockForClose, "Thread should not already have connection lock"); Debug.Assert(firstCall || _connHandler._parserLock.ThreadMayHaveLock(), "Connection lock not taken after the first call"); try { _SqlRPC rpcext = null; int tempLen; // Promote, Commit and Rollback requests for // delegated transactions often happen while there is an open result // set, so we need to handle them by using a different MARS session, // otherwise we'll write on the physical state objects while someone // else is using it. When we don't have MARS enabled, we need to // lock the physical state object to syncronize it's use at least // until we increment the open results count. Once it's been // incremented the delegated transaction requests will fail, so they // won't stomp on anything. if (firstCall) { _connHandler._parserLock.Wait(canReleaseFromAnyThread: !sync); releaseConnectionLock = true; } try { // Ensure that connection is alive if ((TdsParserState.Broken == State) || (TdsParserState.Closed == State)) { throw ADP.ClosedConnectionError(); } // This validation step MUST be done after locking the connection to guarantee we don't // accidentally execute after the transaction has completed on a different thread. if (firstCall) { _asyncWrite = !sync; stateObj.SetTimeoutSeconds(timeout); stateObj.SniContext = SniContext.Snix_Execute; WriteRPCBatchHeaders(stateObj); stateObj._outputMessageType = TdsEnums.MT_RPC; } for (int ii = startRpc; ii < rpcArray.Length; ii++) { rpcext = rpcArray[ii]; if (startParam == 0 || ii > startRpc) { if (rpcext.ProcID != 0) { // Perf optimization for Shiloh and later, Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255"); WriteShort(0xffff, stateObj); WriteShort((short)(rpcext.ProcID), stateObj); } else { Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name"); tempLen = rpcext.rpcName.Length; WriteShort(tempLen, stateObj); WriteString(rpcext.rpcName, tempLen, 0, stateObj); } // Options WriteShort((short)rpcext.options, stateObj); } // Stream out parameters SqlParameter[] parameters = rpcext.parameters; for (int i = (ii == startRpc) ? startParam : 0; i < parameters.Length; i++) { // Debug.WriteLine("i: " + i.ToString(CultureInfo.InvariantCulture)); // parameters can be unnamed SqlParameter param = parameters[i]; // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters. if (param == null) break; // End of parameters for this execute // Validate parameters are not variable length without size and with null value. param.Validate(i, isCommandProc); // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here) MetaType mt = param.InternalMetaType; if (mt.IsNewKatmaiType) { WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj); continue; } if ( (!_isKatmai && !mt.Is90Supported)) { throw ADP.VersionDoesNotSupportDataType(mt.TypeName); } object value = null; bool isNull = true; bool isSqlVal = false; bool isDataFeed = false; // if we have an output param, set the value to null so we do not send it across to the server if (param.Direction == ParameterDirection.Output) { isSqlVal = param.ParamaterIsSqlType; // We have to forward the TYPE info, we need to know what type we are returning. Once we null the paramater we will no longer be able to distinguish what type were seeing. param.Value = null; param.ParamaterIsSqlType = isSqlVal; } else { value = param.GetCoercedValue(); isNull = param.IsNull; if (!isNull) { isSqlVal = param.CoercedValueIsSqlType; isDataFeed = param.CoercedValueIsDataFeed; } } WriteParameterName(param.ParameterNameFixed, stateObj); // Write parameter status stateObj.WriteByte(rpcext.paramoptions[i]); // // fixup the types by using the NullableType property of the MetaType class // // following rules should be followed based on feedback from the M-SQL team // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR) // 2) always use nullable types (ex: instead of SQLINT use SQLINTN) // 3) DECIMALN should always be sent as NUMERICN // stateObj.WriteByte(mt.NullableType); // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns if (mt.TDSType == TdsEnums.SQLVARIANT) { // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant // param.GetActualSize is not used WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj); continue; } // MaxLen field is only written out for non-fixed length data types // use the greater of the two sizes for maxLen int actualSize; int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize(); //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation if (mt.TDSType != TdsEnums.SQLUDT) // getting the actualSize is expensive, cache here and use below actualSize = param.GetActualSize(); else actualSize = 0; //get this later int codePageByteSize = 0; int maxsize = 0; if (mt.IsAnsiType) { // Avoid the following code block if ANSI but unfilled LazyMat blob if ((!isNull) && (!isDataFeed)) { string s; if (isSqlVal) { if (value is SqlString) { s = ((SqlString)value).Value; } else { Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype"); s = new String(((SqlChars)value).Value); } } else { s = (string)value; } codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding); } if (mt.IsPlp) { WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj); } else { maxsize = (size > codePageByteSize) ? size : codePageByteSize; if (maxsize == 0) { // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322) if (mt.IsNCharType) maxsize = 2; else maxsize = 1; } WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj); } } else { // If type timestamp - treat as fixed type and always send over timestamp length, which is 8. // For fixed types, we either send null or fixed length for type length. We want to match that // behavior for timestamps. However, in the case of null, we still must send 8 because if we // send null we will not receive a output val. You can send null for fixed types and still // receive a output value, but not for variable types. So, always send 8 for timestamp because // while the user sees it as a fixed type, we are actually representing it as a bigbinary which // is variable. if (mt.SqlDbType == SqlDbType.Timestamp) { WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj); } else if (mt.SqlDbType == SqlDbType.Udt) { throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); } else if (mt.IsPlp) { if (mt.SqlDbType != SqlDbType.Xml) WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj); } else if ((!mt.IsVarTime) && (mt.SqlDbType != SqlDbType.Date)) { // Time, Date, DateTime2, DateTimeoffset do not have the size written out maxsize = (size > actualSize) ? size : actualSize; if (maxsize == 0) { // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322) if (mt.IsNCharType) maxsize = 2; else maxsize = 1; } WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj); } } // scale and precision are only relevant for numeric and decimal types if (mt.SqlDbType == SqlDbType.Decimal) { byte precision = param.GetActualPrecision(); byte scale = param.GetActualScale(); if (precision > TdsEnums.MAX_NUMERIC_PRECISION) { throw SQL.PrecisionValueOutOfRange(precision); } // Make sure the value matches the scale the user enters if (!isNull) { if (isSqlVal) { value = AdjustSqlDecimalScale((SqlDecimal)value, scale); // If Precision is specified, verify value precision vs param precision if (precision != 0) { if (precision < ((SqlDecimal)value).Precision) { throw ADP.ParameterValueOutOfRange((SqlDecimal)value); } } } else { value = AdjustDecimalScale((Decimal)value, scale); SqlDecimal sqlValue = new SqlDecimal((Decimal)value); // If Precision is specified, verify value precision vs param precision if (precision != 0) { if (precision < sqlValue.Precision) { throw ADP.ParameterValueOutOfRange((Decimal)value); } } } } if (0 == precision) { stateObj.WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION); } else stateObj.WriteByte(precision); stateObj.WriteByte(scale); } else if (mt.IsVarTime) { stateObj.WriteByte(param.GetActualScale()); } // write out collation or xml metadata if (mt.SqlDbType == SqlDbType.Xml) { if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) || ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) || ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))) { stateObj.WriteByte(1); //Schema present flag if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) { tempLen = (param.XmlSchemaCollectionDatabase).Length; stateObj.WriteByte((byte)(tempLen)); WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj); } else { stateObj.WriteByte(0); // No dbname } if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) { tempLen = (param.XmlSchemaCollectionOwningSchema).Length; stateObj.WriteByte((byte)(tempLen)); WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj); } else { stateObj.WriteByte(0); // no xml schema name } if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)) { tempLen = (param.XmlSchemaCollectionName).Length; WriteShort((short)(tempLen), stateObj); WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj); } else { WriteShort(0, stateObj); // No xml schema collection name } } else { stateObj.WriteByte(0); // No schema } } else if (mt.IsCharType) { // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation; Debug.Assert(_defaultCollation != null, "_defaultCollation is null!"); WriteUnsignedInt(outCollation.info, stateObj); stateObj.WriteByte(outCollation.sortId); } if (0 == codePageByteSize) WriteParameterVarLen(mt, actualSize, isNull, stateObj, isDataFeed); else WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj, isDataFeed); Task writeParamTask = null; // write the value now if (!isNull) { if (isSqlVal) { writeParamTask = WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj); } else { // for codePageEncoded types, WriteValue simply expects the number of characters // For plp types, we also need the encoded byte size writeParamTask = WriteValue(value, mt, param.GetActualScale(), actualSize, codePageByteSize, param.Offset, stateObj, param.Size, isDataFeed); } } if (!sync) { if (writeParamTask == null) { writeParamTask = stateObj.WaitForAccumulatedWrites(); } if (writeParamTask != null) { Task task = null; if (completion == null) { completion = new TaskCompletionSource<object>(); task = completion.Task; } AsyncHelper.ContinueTask(writeParamTask, completion, () => TdsExecuteRPC(rpcArray, timeout, inSchema, stateObj, isCommandProc, sync, completion, startRpc: ii, startParam: i + 1), connectionToDoom: _connHandler, onFailure: exc => TdsExecuteRPC_OnFailure(exc, stateObj)); // Take care of releasing the locks if (releaseConnectionLock) { task.ContinueWith(_ => { _connHandler._parserLock.Release(); }, TaskScheduler.Default); releaseConnectionLock = false; } return task; } } #if DEBUG else { Debug.Assert(writeParamTask == null, "Should not have a task when executing sync"); } #endif } // parameter for loop // If this is not the last RPC we are sending, add the batch flag if (ii < (rpcArray.Length - 1)) { stateObj.WriteByte(TdsEnums.YUKON_RPCBATCHFLAG); } } // rpc for loop Task execFlushTask = stateObj.ExecuteFlush(); Debug.Assert(!sync || execFlushTask == null, "Should not get a task when executing sync"); if (execFlushTask != null) { Task task = null; if (completion == null) { completion = new TaskCompletionSource<object>(); task = completion.Task; } bool taskReleaseConnectionLock = releaseConnectionLock; execFlushTask.ContinueWith(tsk => ExecuteFlushTaskCallback(tsk, stateObj, completion, taskReleaseConnectionLock), TaskScheduler.Default); // ExecuteFlushTaskCallback will take care of the locks for us releaseConnectionLock = false; return task; } } catch (Exception e) { if (!ADP.IsCatchableExceptionType(e)) { throw; } FailureCleanup(stateObj, e); throw; } FinalizeExecuteRPC(stateObj); if (completion != null) { completion.SetResult(null); } return null; } catch (Exception e) { FinalizeExecuteRPC(stateObj); if (completion != null) { completion.SetException(e); return null; } else { throw e; } } finally { Debug.Assert(firstCall || !releaseConnectionLock, "Shouldn't be releasing locks synchronously after the first call"); if (releaseConnectionLock) { _connHandler._parserLock.Release(); } } }
internal void WriteSqlVariantHeader(int length, byte tdstype, byte propbytes, TdsParserStateObject stateObj) { WriteInt(length, stateObj); stateObj.WriteByte(tdstype); stateObj.WriteByte(propbytes); }
// Writes metadata portion of parameter stream from an SmiParameterMetaData object. private void WriteSmiParameterMetaData(MSS.SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj) { // Determine status byte status = 0; if (ParameterDirection.Output == metaData.Direction || ParameterDirection.InputOutput == metaData.Direction) { status |= TdsEnums.RPC_PARAM_BYREF; } if (sendDefault) { status |= TdsEnums.RPC_PARAM_DEFAULT; } // Write everything out WriteParameterName(metaData.Name, stateObj); stateObj.WriteByte(status); WriteSmiTypeInfo(metaData, stateObj); }
internal void WriteSqlVariantDateTime2(DateTime value, TdsParserStateObject stateObj) { MSS.SmiMetaData dateTime2MetaData = MSS.SmiMetaData.DefaultDateTime2; // NOTE: 3 bytes added here to support additional header information for variant - internal type, scale prop, scale WriteSqlVariantHeader((int)(dateTime2MetaData.MaxLength + 3), TdsEnums.SQLDATETIME2, 1 /* one scale prop */, stateObj); stateObj.WriteByte(dateTime2MetaData.Scale); //scale property WriteDateTime2(value, dateTime2MetaData.Scale, (int)(dateTime2MetaData.MaxLength), stateObj); }
private void WriteTvpTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { Debug.Assert(SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued, "Invalid metadata for TVPs. Type=" + metaData.SqlDbType); // Type token stateObj.WriteByte((byte)TdsEnums.SQLTABLE); // 3-part name (DB, Schema, TypeName) WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj); WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj); // TVP_COLMETADATA if (0 == metaData.FieldMetaData.Count) { WriteUnsignedShort((ushort)TdsEnums.TVP_NOMETADATA_TOKEN, stateObj); } else { // COUNT of columns WriteUnsignedShort(checked((ushort)metaData.FieldMetaData.Count), stateObj); // TvpColumnMetaData for each column (look for defaults in this loop MSS.SmiDefaultFieldsProperty defaults = (MSS.SmiDefaultFieldsProperty)metaData.ExtendedProperties[MSS.SmiPropertySelector.DefaultFields]; for (int i = 0; i < metaData.FieldMetaData.Count; i++) { WriteTvpColumnMetaData(metaData.FieldMetaData[i], defaults[i], stateObj); } // optional OrderUnique metadata WriteTvpOrderUnique(metaData, stateObj); } // END of optional metadata stateObj.WriteByte(TdsEnums.TVP_END_TOKEN); }
private void WriteDateTimeOffset(DateTimeOffset value, byte scale, int length, TdsParserStateObject stateObj) { WriteDateTime2(value.UtcDateTime, scale, length - 2, stateObj); Int16 offset = (Int16)value.Offset.TotalMinutes; stateObj.WriteByte((byte)(offset & 0xff)); stateObj.WriteByte((byte)((offset >> 8) & 0xff)); }
internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) { // Write DONE packet // if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) { throw ADP.ClosedConnectionError(); } stateObj.WriteByte(TdsEnums.SQLDONE); WriteShort(0, stateObj); WriteShort(0, stateObj); WriteInt(0, stateObj); stateObj._pendingData = true; stateObj._messageStatus = 0; return stateObj.WritePacket(TdsEnums.HARDFLUSH); }
internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) { // sign if (d.IsPositive) stateObj.WriteByte(1); else stateObj.WriteByte(0); WriteUnsignedInt(d.m_data1, stateObj); WriteUnsignedInt(d.m_data2, stateObj); WriteUnsignedInt(d.m_data3, stateObj); WriteUnsignedInt(d.m_data4, stateObj); }
internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) { Debug.Assert(!isSqlType || value is INullable, "isSqlType is true, but value can not be type cast to an INullable"); Debug.Assert(!isDataFeed ^ value is DataFeed, "Incorrect value for isDataFeed"); Encoding saveEncoding = _defaultEncoding; SqlCollation saveCollation = _defaultCollation; int saveCodePage = _defaultCodePage; int saveLCID = _defaultLCID; Task resultTask = null; Task internalWriteTask = null; if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) { throw ADP.ClosedConnectionError(); } try { if (metadata.encoding != null) { _defaultEncoding = metadata.encoding; } if (metadata.collation != null) { _defaultCollation = metadata.collation; _defaultLCID = _defaultCollation.LCID; } _defaultCodePage = metadata.codePage; MetaType metatype = metadata.metaType; int ccb = 0; int ccbStringBytes = 0; if (isNull) { // For UDT, remember we treat as binary even though it is a PLP if (metatype.IsPlp && (metatype.NullableType != TdsEnums.SQLUDT || metatype.IsLong)) { WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj); } else if (!metatype.IsFixed && !metatype.IsLong && !metatype.IsVarTime) { WriteShort(TdsEnums.VARNULL, stateObj); } else { stateObj.WriteByte(TdsEnums.FIXEDNULL); } return resultTask; } if (!isDataFeed) { switch (metatype.NullableType) { case TdsEnums.SQLBIGBINARY: case TdsEnums.SQLBIGVARBINARY: case TdsEnums.SQLIMAGE: case TdsEnums.SQLUDT: ccb = (isSqlType) ? ((SqlBinary)value).Length : ((byte[])value).Length; break; case TdsEnums.SQLUNIQUEID: ccb = GUID_SIZE; // that's a constant for guid break; case TdsEnums.SQLBIGCHAR: case TdsEnums.SQLBIGVARCHAR: case TdsEnums.SQLTEXT: if (null == _defaultEncoding) { ThrowUnsupportedCollationEncountered(null); // stateObject only when reading } string stringValue = null; if (isSqlType) { stringValue = ((SqlString)value).Value; } else { stringValue = (string)value; } ccb = stringValue.Length; ccbStringBytes = _defaultEncoding.GetByteCount(stringValue); break; case TdsEnums.SQLNCHAR: case TdsEnums.SQLNVARCHAR: case TdsEnums.SQLNTEXT: ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2; break; case TdsEnums.SQLXMLTYPE: // Value here could be string or XmlReader if (value is XmlReader) { value = MetaType.GetStringFromXml((XmlReader)value); } ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2; break; default: ccb = metadata.length; break; } } else { Debug.Assert(metatype.IsLong && ((metatype.SqlDbType == SqlDbType.VarBinary && value is StreamDataFeed) || ((metatype.SqlDbType == SqlDbType.VarChar || metatype.SqlDbType == SqlDbType.NVarChar) && value is TextDataFeed) || (metatype.SqlDbType == SqlDbType.Xml && value is XmlDataFeed)), "Stream data feed should only be assigned to VarBinary(max), Text data feed should only be assigned to [N]VarChar(max), Xml data feed should only be assigned to XML(max)"); } // Expected the text length in data stream for bulk copy of text, ntext, or image data. // if (metatype.IsLong) { switch (metatype.SqlDbType) { case SqlDbType.Text: case SqlDbType.NText: case SqlDbType.Image: stateObj.WriteByteArray(s_longDataHeader, s_longDataHeader.Length, 0); WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj); break; case SqlDbType.VarChar: case SqlDbType.NVarChar: case SqlDbType.VarBinary: case SqlDbType.Xml: case SqlDbType.Udt: // plp data WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj); break; } } else { WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj); } if (isSqlType) { internalWriteTask = WriteSqlValue(value, metatype, ccb, ccbStringBytes, 0, stateObj); } else if (metatype.SqlDbType != SqlDbType.Udt || metatype.IsLong) { internalWriteTask = WriteValue(value, metatype, metadata.scale, ccb, ccbStringBytes, 0, stateObj, metadata.length, isDataFeed); if ((internalWriteTask == null) && (_asyncWrite)) { internalWriteTask = stateObj.WaitForAccumulatedWrites(); } Debug.Assert(_asyncWrite || stateObj.WaitForAccumulatedWrites() == null, "Should not have accumulated writes when writing sync"); } else { WriteShort(ccb, stateObj); internalWriteTask = stateObj.WriteByteArray((byte[])value, ccb, 0); } #if DEBUG //In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths. if (_asyncWrite && internalWriteTask == null && SqlBulkCopy.SetAlwaysTaskOnWrite == true) { internalWriteTask = Task.FromResult<object>(null); } #endif if (internalWriteTask != null) { //i.e. the write was async. resultTask = WriteBulkCopyValueSetupContinuation(internalWriteTask, saveEncoding, saveCollation, saveCodePage, saveLCID); } } finally { if (internalWriteTask == null) { _defaultEncoding = saveEncoding; _defaultCollation = saveCollation; _defaultCodePage = saveCodePage; _defaultLCID = saveLCID; } } return resultTask; }
private void WriteDecimal(decimal value, TdsParserStateObject stateObj) { stateObj._decimalBits = Decimal.GetBits(value); Debug.Assert(null != stateObj._decimalBits, "decimalBits should be filled in at TdsExecuteRPC time"); /* Returns a binary representation of a Decimal. The return value is an integer array with four elements. Elements 0, 1, and 2 contain the low, middle, and high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains the scale factor and sign of the Decimal: bits 0-15 (the lower word) are unused; bits 16-23 contain a value between 0 and 28, indicating the power of 10 to divide the 96-bit integer part by to produce the Decimal value; bits 24- 30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0 meaning positive and 1 meaning negative. SQLDECIMAL/SQLNUMERIC has a byte stream of: struct { BYTE sign; // 1 if positive, 0 if negative BYTE data[]; } For TDS 7.0 and above, there are always 17 bytes of data */ // write the sign (note that COM and SQL are opposite) if (0x80000000 == (stateObj._decimalBits[3] & 0x80000000)) stateObj.WriteByte(0); else stateObj.WriteByte(1); WriteInt(stateObj._decimalBits[0], stateObj); WriteInt(stateObj._decimalBits[1], stateObj); WriteInt(stateObj._decimalBits[2], stateObj); WriteInt(0, stateObj); }
// For MAX types, this method can only write everything in one big chunk. If multiple // chunk writes needed, please use WritePlpBytes/WritePlpChars private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) { Debug.Assert(((type.NullableType == TdsEnums.SQLXMLTYPE) || (value is INullable && !((INullable)value).IsNull)), "unexpected null SqlType!"); // parameters are always sent over as BIG or N types switch (type.NullableType) { case TdsEnums.SQLFLTN: if (type.FixedLength == 4) WriteFloat(((SqlSingle)value).Value, stateObj); else { Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!"); WriteDouble(((SqlDouble)value).Value, stateObj); } break; case TdsEnums.SQLBIGBINARY: case TdsEnums.SQLBIGVARBINARY: case TdsEnums.SQLIMAGE: { if (type.IsPlp) { WriteInt(actualLength, stateObj); // chunk length } if (value is SqlBinary) { return stateObj.WriteByteArray(((SqlBinary)value).Value, actualLength, offset, canAccumulate: false); } else { Debug.Assert(value is SqlBytes); return stateObj.WriteByteArray(((SqlBytes)value).Value, actualLength, offset, canAccumulate: false); } } case TdsEnums.SQLUNIQUEID: { byte[] b = ((SqlGuid)value).ToByteArray(); Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object"); stateObj.WriteByteArray(b, actualLength, 0); break; } case TdsEnums.SQLBITN: { Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type"); if (((SqlBoolean)value).Value == true) stateObj.WriteByte(1); else stateObj.WriteByte(0); break; } case TdsEnums.SQLINTN: if (type.FixedLength == 1) stateObj.WriteByte(((SqlByte)value).Value); else if (type.FixedLength == 2) WriteShort(((SqlInt16)value).Value, stateObj); else if (type.FixedLength == 4) WriteInt(((SqlInt32)value).Value, stateObj); else { Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type: " + type.FixedLength.ToString(CultureInfo.InvariantCulture)); WriteLong(((SqlInt64)value).Value, stateObj); } break; case TdsEnums.SQLBIGCHAR: case TdsEnums.SQLBIGVARCHAR: case TdsEnums.SQLTEXT: if (type.IsPlp) { WriteInt(codePageByteSize, stateObj); // chunk length } if (value is SqlChars) { String sch = new String(((SqlChars)value).Value); return WriteEncodingChar(sch, actualLength, offset, _defaultEncoding, stateObj, canAccumulate: false); } else { Debug.Assert(value is SqlString); return WriteEncodingChar(((SqlString)value).Value, actualLength, offset, _defaultEncoding, stateObj, canAccumulate: false); } case TdsEnums.SQLNCHAR: case TdsEnums.SQLNVARCHAR: case TdsEnums.SQLNTEXT: case TdsEnums.SQLXMLTYPE: if (type.IsPlp) { if (IsBOMNeeded(type, value)) { WriteInt(actualLength + 2, stateObj); // chunk length WriteShort(TdsEnums.XMLUNICODEBOM, stateObj); } else { WriteInt(actualLength, stateObj); // chunk length } } // convert to cchars instead of cbytes // Xml type is already converted to string through GetCoercedValue if (actualLength != 0) actualLength >>= 1; if (value is SqlChars) { return WriteCharArray(((SqlChars)value).Value, actualLength, offset, stateObj, canAccumulate: false); } else { Debug.Assert(value is SqlString); return WriteString(((SqlString)value).Value, actualLength, offset, stateObj, canAccumulate: false); } case TdsEnums.SQLNUMERICN: Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes"); WriteSqlDecimal((SqlDecimal)value, stateObj); break; case TdsEnums.SQLDATETIMN: SqlDateTime dt = (SqlDateTime)value; if (type.FixedLength == 4) { if (0 > dt.DayTicks || dt.DayTicks > UInt16.MaxValue) throw SQL.SmallDateTimeOverflow(dt.ToString()); WriteShort(dt.DayTicks, stateObj); WriteShort(dt.TimeTicks / SqlDateTime.SQLTicksPerMinute, stateObj); } else { WriteInt(dt.DayTicks, stateObj); WriteInt(dt.TimeTicks, stateObj); } break; case TdsEnums.SQLMONEYN: { WriteSqlMoney((SqlMoney)value, type.FixedLength, stateObj); break; } case TdsEnums.SQLUDT: throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); default: Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); break; } // switch // return point for accumualated writes, note: non-accumulated writes returned from their case statements return null; }
private void WriteIdentifier(string s, TdsParserStateObject stateObj) { if (null != s) { stateObj.WriteByte(checked((byte)s.Length)); WriteString(s, stateObj); } else { stateObj.WriteByte((byte)0); } }
// // we always send over nullable types for parameters so we always write the varlen fields // internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsParserStateObject stateObj, bool unknownLength = false) { if (type.IsLong) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths if (isNull) { if (type.IsPlp) { WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj); } else { WriteInt(unchecked((int)TdsEnums.VARLONGNULL), stateObj); } } else if (type.NullableType == TdsEnums.SQLXMLTYPE || unknownLength) { WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj); } else if (type.IsPlp) { // Non-xml plp types WriteLong((long)size, stateObj); } else { WriteInt(size, stateObj); } } else if (type.IsVarTime) { if (isNull) { stateObj.WriteByte(TdsEnums.FIXEDNULL); } else { stateObj.WriteByte((byte)size); } } else if (false == type.IsFixed) { // non-long but variable length column, must be a BIG* type: 2 byte length if (isNull) { WriteShort(TdsEnums.VARNULL, stateObj); } else { WriteShort(size, stateObj); } } else { if (isNull) { stateObj.WriteByte(TdsEnums.FIXEDNULL); } else { Debug.Assert(type.FixedLength <= 0xff, "WriteParameterVarLen: invalid one byte length!"); stateObj.WriteByte((byte)(type.FixedLength & 0xff)); // 1 byte for everything else } } }
/// <summary> /// Writes the UserType and TYPE_INFO values for CryptoMetadata (for bulk copy). /// </summary> /// <returns></returns> internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserStateObject stateObj) { // Write the UserType (4 byte value) WriteInt(0x0, stateObj); // TODO: fix this- timestamp columns have 0x50 value here Debug.Assert(SqlDbType.Xml != mdPriv.type); Debug.Assert(SqlDbType.Udt != mdPriv.type); stateObj.WriteByte(mdPriv.tdsType); switch (mdPriv.type) { case SqlDbType.Decimal: WriteTokenLength(mdPriv.tdsType, mdPriv.length, stateObj); stateObj.WriteByte(mdPriv.precision); stateObj.WriteByte(mdPriv.scale); break; case SqlDbType.Date: // Nothing more to write! break; case SqlDbType.Time: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: stateObj.WriteByte(mdPriv.scale); break; default: WriteTokenLength(mdPriv.tdsType, mdPriv.length, stateObj); if (mdPriv.metaType.IsCharType && _isShiloh) { WriteUnsignedInt(mdPriv.collation.info, stateObj); stateObj.WriteByte(mdPriv.collation.sortId); } break; } }