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