Example #1
0
        // 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;
        }
Example #2
0
        // 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));
        }
Example #3
0
        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
        }
Example #4
0
        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;
        }
Example #5
0
        internal Task WriteString(string s, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true)
        {
            int cBytes = ADP.CharSize * length;

            // Perf shortcut: If it fits, write directly to the outBuff
            if (cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed))
            {
                CopyStringToBytes(s, offset, stateObj._outBuff, stateObj._outBytesUsed, length);
                stateObj._outBytesUsed += cBytes;
                return null;
            }
            else
            {
                if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes)
                {
                    stateObj._bTmp = new byte[cBytes];
                }

                CopyStringToBytes(s, offset, stateObj._bTmp, 0, length);
                return stateObj.WriteByteArray(stateObj._bTmp, cBytes, 0, canAccumulate);
            }
        }
Example #6
0
        private Task WriteEncodingChar(string s, int numChars, int offset, Encoding encoding, TdsParserStateObject stateObj, bool canAccumulate = true)
        {
            char[] charData;
            byte[] byteData;

            // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
            // 7.0 has no support for multiple code pages in data - single code page support only
            if (encoding == null)
                encoding = _defaultEncoding;

            charData = s.ToCharArray(offset, numChars);

            // Optimization: if the entire string fits in the current buffer, then copy it directly
            int bytesLeft = stateObj._outBuff.Length - stateObj._outBytesUsed;
            if ((numChars <= bytesLeft) && (encoding.GetMaxByteCount(charData.Length) <= bytesLeft))
            {
                int bytesWritten = encoding.GetBytes(charData, 0, charData.Length, stateObj._outBuff, stateObj._outBytesUsed);
                stateObj._outBytesUsed += bytesWritten;
                return null;
            }
            else
            {
                byteData = encoding.GetBytes(charData, 0, numChars);
                Debug.Assert(byteData != null, "no data from encoding");
                return stateObj.WriteByteArray(byteData, byteData.Length, 0, canAccumulate);
            }
        }
Example #7
0
        //
        // 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;
        }
Example #8
0
        // 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;
        }
Example #9
0
        // Write the trace header data, not including the trace header length
        private void WriteTraceHeaderData(TdsParserStateObject stateObj) {
            Debug.Assert(this.IncludeTraceHeader, "WriteTraceHeaderData can only be called on a Denali or higher version server and bid trace with the control bit are on");

            // We may need to update the trace header length if trace header is changed in the future

            ActivityCorrelator.ActivityId actId = ActivityCorrelator.Current;

            WriteShort(TdsEnums.HEADERTYPE_TRACE, stateObj);      // Trace Header Type

            stateObj.WriteByteArray(actId.Id.ToByteArray(), GUID_SIZE, 0); // Id (Guid)
            WriteUnsignedInt(actId.Sequence, stateObj); // sequence number

            Bid.Trace("<sc.TdsParser.WriteTraceHeaderData|INFO> ActivityID %ls\n", actId.ToString());
        }
Example #10
0
        //
        // Takes a double and writes it as a 64 bit double.
        //
        internal void WriteDouble(double v, TdsParserStateObject stateObj)
        {
            byte[] bytes = BitConverter.GetBytes(v);

            stateObj.WriteByteArray(bytes, bytes.Length, 0);
        }
Example #11
0
        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
        }
Example #12
0
        /// <summary>
        /// Writes a single entry of CEK Table into TDS Stream (for bulk copy).
        /// </summary>
        /// <returns></returns>
        internal void WriteEncryptionEntries (ref SqlTceCipherInfoTable cekTable, TdsParserStateObject stateObj) {
            for (int i =0; i < cekTable.Size; i++) {
                // Write Db ID
                WriteInt(cekTable[i].DatabaseId, stateObj);

                // Write Key ID
                WriteInt(cekTable[i].CekId, stateObj);

                // Write Key Version
                WriteInt(cekTable[i].CekVersion, stateObj);

                // Write 8 bytes of key MD Version
                Debug.Assert (8 == cekTable[i].CekMdVersion.Length);
                stateObj.WriteByteArray (cekTable[i].CekMdVersion, 8, 0);

                // We don't really need to send the keys
                stateObj.WriteByte(0x00);
            }
        }
Example #13
0
      internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, 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(cmd != null, @"cmd cannot be null inside TdsExecuteRPC");
          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;

              // SQLBUDT #20010853 - 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;

                      _connHandler.CheckEnlistedTransactionBinding();

                      stateObj.SetTimeoutSeconds(timeout);
                      if ((!_fMARS) && (_physicalStateObj.HasOpenResult)) 
                      {
                          Bid.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID);
                      }
                      stateObj.SniContext = SniContext.Snix_Execute;

                      if (_isYukon) {

                          WriteRPCBatchHeaders(stateObj, notificationRequest);
                      }

                      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 && _isShiloh) {
                              // 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

                          // Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
                          if (param.ForceColumnEncryption && 
                              !(cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled || 
                              (cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && cmd.Connection.IsColumnEncryptionSettingEnabled))) {
                              throw SQL.ParamInvalidForceColumnEncryptionSetting(param.ParameterName, rpcext.GetCommandTextOrRpcName());
                          }

                          // Check if the applications wants to force column encryption to avoid sending sensitive data to server
                          if (param.ForceColumnEncryption && param.CipherMetadata == null
                              && (param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput)) {
                              // Application wants a parameter to be encrypted before sending it to server, however server doesnt think this parameter needs encryption.
                              throw SQL.ParamUnExpectedEncryptionMetadata(param.ParameterName, rpcext.GetCommandTextOrRpcName());
                          }

                          // Validate parameters are not variable length without size and with null value.  MDAC 66522
                          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 ((!_isShiloh && !mt.Is70Supported) ||
                              (!_isYukon && !mt.Is80Supported) ||
                              (!_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]);

                          // 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

                          byte precision = 0;
                          byte scale = 0;

                          // scale and precision are only relevant for numeric and decimal types
                          // adjust the actual value scale and precision to match the user specified
                          if (mt.SqlDbType == SqlDbType.Decimal) {
                              precision = param.GetActualPrecision();
                              scale = param.GetActualScale();

                              if (precision > TdsEnums.MAX_NUMERIC_PRECISION) {
                                  throw SQL.PrecisionValueOutOfRange(precision);
                              }

                              // bug 49512, 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);
                                          }
                                      }
                                  }
                              }
                          }

                          bool isParameterEncrypted = 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_ENCRYPTED);

                          // Additional information we need to send over wire to the server when writing encrypted parameters.
                          SqlColumnEncryptionInputParameterInfo encryptedParameterInfoToWrite = null;

                          // If the parameter is encrypted, we need to encrypt the value.
                          if (isParameterEncrypted) {
                              Debug.Assert(mt.TDSType != TdsEnums.SQLVARIANT &&
                                  mt.TDSType != TdsEnums.SQLUDT &&
                                  mt.TDSType != TdsEnums.SQLXMLTYPE &&
                                  mt.TDSType != TdsEnums.SQLIMAGE &&
                                  mt.TDSType != TdsEnums.SQLTEXT &&
                                  mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");

                              byte[] serializedValue = null;
                              byte[] encryptedValue = null;

                              if (!isNull) {
                                  try {
                                    if (isSqlVal) {
                                        serializedValue = SerializeUnencryptedSqlValue(value, mt, actualSize, param.Offset, param.NormalizationRuleVersion, stateObj);
                                    }
                                    else {
                                        // for codePageEncoded types, WriteValue simply expects the number of characters
                                        // For plp types, we also need the encoded byte size
                                        serializedValue = SerializeUnencryptedValue(value, mt, param.GetActualScale(), actualSize, param.Offset, isDataFeed, param.NormalizationRuleVersion, stateObj);
                                    }

                                    Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC.");
                                    encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.ConnectionOptions.DataSource);
                                }
                                catch (Exception e) {
                                    throw SQL.ParamEncryptionFailed(param.ParameterName, null, e);
                                }

                                  Debug.Assert(encryptedValue != null && encryptedValue.Length > 0,
                                      "encryptedValue should not be null or empty in TdsExecuteRPC.");
                              }
                              else {
                                  encryptedValue = null;
                              }

                              // Change the datatype to varbinary(max).
                              // Since we don't know the size of the encrypted parameter on the server side, always set to (max).
                              //
                              mt = MetaType.MetaMaxVarBinary;
                              size = -1;
                              actualSize = (encryptedValue == null) ? 0 : encryptedValue.Length;

                              encryptedParameterInfoToWrite = new SqlColumnEncryptionInputParameterInfo(param.GetMetadataForTypeInfo(),
                                                                                                        param.CipherMetadata);

                              // Set the value to the encrypted value and mark isSqlVal as false for VARBINARY encrypted value.
                              value = encryptedValue;
                              isSqlVal = false;
                          }

                          Debug.Assert(isParameterEncrypted == (encryptedParameterInfoToWrite != null),
                                            "encryptedParameterInfoToWrite can be not null if and only if isParameterEncrypted is true.");

                          Debug.Assert(!isSqlVal || !isParameterEncrypted, "isParameterEncrypted can be true only if isSqlVal is false.");

                          //
                          // 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;
                          }

                          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) {
                                  byte[] udtVal = null;
                                  Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;

                                  Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!");

                                  if (!isNull) {
                                      udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize);

                                      Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value");
                                      size = udtVal.Length;

                                      //it may be legitimate, but we dont support it yet
                                      if (size < 0 || (size >= UInt16.MaxValue && maxsize != -1))
                                          throw new IndexOutOfRangeException();
                                  }

                                  //if this is NULL value, write special null value
                                  byte[] lenBytes = BitConverter.GetBytes((Int64)size);

                                  if (ADP.IsEmpty(param.UdtTypeName))
                                      throw SQL.MustSetUdtTypeNameForUdtParams();

                                  // Split the input name. TypeName is returned as single 3 part name during DeriveParameters.
                                  // NOTE: ParseUdtTypeName throws if format is incorrect
                                  String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, true /* is UdtTypeName */);
                                  if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) {
                                      throw ADP.ArgumentOutOfRange("names");
                                  }
                                  if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) {
                                      throw ADP.ArgumentOutOfRange("names");
                                  }
                                  if (TdsEnums.MAX_SERVERNAME < names[2].Length) {
                                      throw ADP.ArgumentOutOfRange("names");
                                  }

                                  WriteUDTMetaData(value, names[0], names[1], names[2], stateObj);

                                  // 
                                  if (!isNull) {
                                      WriteUnsignedLong((ulong)udtVal.Length, stateObj); // PLP length
                                      if (udtVal.Length > 0) { // Only write chunk length if its value is greater than 0
                                          WriteInt(udtVal.Length, stateObj);                  // Chunk length
                                          stateObj.WriteByteArray(udtVal, udtVal.Length, 0); // Value
                                      }
                                      WriteInt(0, stateObj);                              // Terminator
                                  }
                                  else {
                                      WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); // PLP Null.
                                  }
                                  continue; // End of UDT - continue to next parameter.
                                  // 
                              }
                              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 && IsYukonOrNewer) {
                                      // 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) {
                              if (0 == precision) {
                                  if (_isShiloh)
                                      stateObj.WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION);
                                  else
                                      stateObj.WriteByte(TdsEnums.SPHINX_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 (_isYukon && (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 (_isShiloh && 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, isParameterEncrypted ? (byte)0 : param.GetActualScale(), actualSize, codePageByteSize, isParameterEncrypted ? 0 : param.Offset, stateObj, isParameterEncrypted ? 0 : param.Size, isDataFeed);
                              }
                          }

                          // Send encryption metadata for encrypted parameters.
                          if (isParameterEncrypted) {
                              writeParamTask = WriteEncryptionMetadata(writeParamTask, encryptedParameterInfoToWrite, stateObj);
                          }

                          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(cmd, rpcArray, timeout, inSchema, notificationRequest, 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)) {
                          if (_isYukon) {
                              stateObj.WriteByte(TdsEnums.YUKON_RPCBATCHFLAG);

                          }
                          else {
                              stateObj.WriteByte(TdsEnums.SHILOH_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();
              }
          }
      }
Example #14
0
        internal SqlDataReader TdsExecuteTransactionManagerRequest(
                    byte[] buffer,
                    TdsEnums.TransactionManagerRequestType request,
                    string transactionName,
                    TdsEnums.TransactionManagerIsolationLevel isoLevel,
                    int timeout,
                    SqlInternalTransaction transaction,
                    TdsParserStateObject stateObj,
                    bool isDelegateControlRequest) {

            Debug.Assert(this == stateObj.Parser, "different parsers");

            if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
                return null;
            }

            // SQLBUDT #20010853 - 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;

                // 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 (!isDelegateControlRequest) {
                    _connHandler.CheckEnlistedTransactionBinding();
                }

                stateObj._outputMessageType = TdsEnums.MT_TRANS;       // set message type
                stateObj.SetTimeoutSeconds(timeout);

                stateObj.SniContext = SniContext.Snix_Execute;

                if (_isYukon) {
                    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.GetDTCAddress:
                        WriteShort(0, stateObj);

                        returnReader = true;
                        break;
                    case TdsEnums.TransactionManagerRequestType.Propagate:
                        if (null != buffer) {
                            WriteShort(buffer.Length, stateObj);
                            stateObj.WriteByteArray(buffer, buffer.Length, 0);
                        }
                        else {
                            WriteShort(0, stateObj);
                        }
                        break;
                    case TdsEnums.TransactionManagerRequestType.Begin:
                        Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
                        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.  See SQL BU DT 345300 for full details and repro.

                        // 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.Promote:
                        Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
                        // No payload - except current transaction in header
                        // Promote returns a DTC cookie.  However, the transaction cookie we use for the
                        // connection does not change after a promote.
                        break;
                    case TdsEnums.TransactionManagerRequestType.Commit:
                        Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");

                        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:
                        Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");

                        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:
                        Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");

                        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);
                }

                // If the retained ID is no longer valid (because we are enlisting in null or a new transaction) then it should be cleared
                if (((request == TdsEnums.TransactionManagerRequestType.Begin) || (request == TdsEnums.TransactionManagerRequestType.Propagate)) && ((transaction == null) || (transaction.TransactionId != _retainedTransactionId))) {
                    _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
                }

                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();
                }
            }
        }
Example #15
0
        /// <summary>
        /// Write parameter encryption metadata.
        /// </summary>
        private void WriteEncryptionMetadata(SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo, TdsParserStateObject stateObj) {
            Debug.Assert(columnEncryptionParameterInfo != null, @"columnEncryptionParameterInfo cannot be null");
            Debug.Assert(stateObj != null, @"stateObj cannot be null");

            // Write the TypeInfo.
            WriteSmiTypeInfo(columnEncryptionParameterInfo.ParameterMetadata, stateObj);

            // Write the serialized array in columnEncryptionParameterInfo.
            stateObj.WriteByteArray(columnEncryptionParameterInfo.SerializedWireFormat,
                                    columnEncryptionParameterInfo.SerializedWireFormat.Length,
                                    offsetBuffer: 0);
        }