SqlCachedBuffer.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. //------------------------------------------------------------------------------
  2. // <copyright file="SqlCachedBuffer.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.SqlClient {
  9. using System;
  10. using System.Collections.Generic;
  11. using System.ComponentModel;
  12. using System.Data;
  13. using System.Data.Common;
  14. using System.Diagnostics;
  15. using System.Globalization;
  16. using System.Text;
  17. using System.Xml;
  18. using System.Data.SqlTypes;
  19. using System.IO;
  20. using System.Runtime.InteropServices;
  21. using System.Reflection;
  22. using System.Runtime.CompilerServices;
  23. // Caches the bytes returned from partial length prefixed datatypes, like XML
  24. sealed internal class SqlCachedBuffer : System.Data.SqlTypes.INullable{
  25. public static readonly SqlCachedBuffer Null = new SqlCachedBuffer();
  26. private const int _maxChunkSize = 2048; // Arbitrary value for chunk size. Revisit this later for better perf
  27. private List<byte[]> _cachedBytes;
  28. private SqlCachedBuffer() {
  29. // For constructing Null
  30. }
  31. private SqlCachedBuffer(List<byte[]> cachedBytes) {
  32. _cachedBytes = cachedBytes;
  33. }
  34. internal List<byte[]> CachedBytes {
  35. get { return _cachedBytes; }
  36. }
  37. // Reads off from the network buffer and caches bytes. Only reads one column value in the current row.
  38. static internal bool TryCreate(SqlMetaDataPriv metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer) {
  39. int cb = 0;
  40. ulong plplength;
  41. byte[] byteArr;
  42. List<byte[]> cachedBytes = new List<byte[]>();
  43. buffer = null;
  44. // the very first length is already read.
  45. if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
  46. return false;
  47. }
  48. // For now we only handle Plp data from the parser directly.
  49. Debug.Assert(metadata.metaType.IsPlp, "SqlCachedBuffer call on a non-plp data");
  50. do {
  51. if (plplength == 0)
  52. break;
  53. do {
  54. cb = (plplength > (ulong) _maxChunkSize) ? _maxChunkSize : (int)plplength ;
  55. byteArr = new byte[cb];
  56. if (!stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb)) {
  57. return false;
  58. }
  59. Debug.Assert(cb == byteArr.Length);
  60. if (cachedBytes.Count == 0) {
  61. // Add the Byte order mark if needed if we read the first array
  62. AddByteOrderMark(byteArr, cachedBytes);
  63. }
  64. cachedBytes.Add(byteArr);
  65. plplength -= (ulong)cb;
  66. } while (plplength > 0);
  67. if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
  68. return false;
  69. }
  70. } while (plplength > 0);
  71. Debug.Assert(stateObj._longlen == 0 && stateObj._longlenleft == 0);
  72. buffer = new SqlCachedBuffer(cachedBytes);
  73. return true;
  74. }
  75. private static void AddByteOrderMark(byte[] byteArr, List<byte[]> cachedBytes) {
  76. // Need to find out if we should add byte order mark or not.
  77. // We need to add this if we are getting ntext xml, not if we are getting binary xml
  78. // Binary Xml always begins with the bytes 0xDF and 0xFF
  79. // If we aren't getting these, then we are getting unicode xml
  80. if ((byteArr.Length < 2 ) || (byteArr[0] != 0xDF) || (byteArr[1] != 0xFF)){
  81. Debug.Assert(cachedBytes.Count == 0);
  82. cachedBytes.Add(TdsEnums.XMLUNICODEBOMBYTES);
  83. }
  84. }
  85. internal Stream ToStream() {
  86. return new SqlCachedStream(this);
  87. }
  88. override public string ToString() {
  89. if (IsNull)
  90. throw new SqlNullValueException();
  91. if (_cachedBytes.Count == 0) {
  92. return String.Empty;
  93. }
  94. SqlXml sxml = new SqlXml(ToStream());
  95. return sxml.Value;
  96. }
  97. internal SqlString ToSqlString() {
  98. if (IsNull)
  99. return SqlString.Null;
  100. string str = ToString();
  101. return new SqlString(str);
  102. }
  103. internal SqlXml ToSqlXml() {
  104. SqlXml sx = new SqlXml(ToStream());
  105. return sx;
  106. }
  107. // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
  108. [MethodImpl(MethodImplOptions.NoInlining)]
  109. internal XmlReader ToXmlReader() {
  110. //XmlTextReader xr = new XmlTextReader(fragment, XmlNodeType.Element, null);
  111. XmlReaderSettings readerSettings = new XmlReaderSettings();
  112. readerSettings.ConformanceLevel = ConformanceLevel.Fragment;
  113. // Call internal XmlReader.CreateSqlReader from System.Xml.
  114. // Signature: internal static XmlReader CreateSqlReader(Stream input, XmlReaderSettings settings, XmlParserContext inputContext);
  115. MethodInfo createSqlReaderMethodInfo = typeof(System.Xml.XmlReader).GetMethod("CreateSqlReader", BindingFlags.Static | BindingFlags.NonPublic);
  116. object[] args = new object[3] { ToStream(), readerSettings, null };
  117. XmlReader xr;
  118. new System.Security.Permissions.ReflectionPermission(System.Security.Permissions.ReflectionPermissionFlag.MemberAccess).Assert();
  119. try {
  120. xr = (XmlReader)createSqlReaderMethodInfo.Invoke(null, args);
  121. }
  122. finally {
  123. System.Security.Permissions.ReflectionPermission.RevertAssert();
  124. }
  125. return xr;
  126. }
  127. public bool IsNull {
  128. get {
  129. return (_cachedBytes == null) ? true : false ;
  130. }
  131. }
  132. }
  133. }