157 lines
6.2 KiB
C#
157 lines
6.2 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="SqlCachedBuffer.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</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 ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|