Format Cell Values (#62)

* WIP for ability to localize cell values

* Changing how DateTimeOffsets are stored, getting unit tests going

* Reworking BufferFileStreamWriter to use dictionary approach

* Plumbing the DbCellValue type the rest of the way through

* Removing unused components to simplify contract

* Cleanup and making sure byte[] appears in parity with SSMS

* CR comments, small tweaks for optimizing LINQ
This commit is contained in:
Benjamin Russell
2016-09-22 12:00:32 -07:00
committed by GitHub
parent 1f0240dbe0
commit 93a75f1ff4
13 changed files with 535 additions and 977 deletions

View File

@@ -3,25 +3,16 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
/// <summary>
/// Represents a value returned from a read from a file stream. This is used to eliminate ref
/// parameters used in the read methods.
/// </summary>
/// <typeparam name="T">The type of the value that was read</typeparam>
public struct FileStreamReadResult<T>
public struct FileStreamReadResult
{
/// <summary>
/// Whether or not the value of the field is null
/// </summary>
public bool IsNull { get; set; }
/// <summary>
/// The value of the field. If <see cref="IsNull"/> is true, this will be set to <c>default(T)</c>
/// </summary>
public T Value { get; set; }
/// <summary>
/// The total length in bytes of the value, (including the bytes used to store the length
/// of the value)
@@ -34,17 +25,20 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </remarks>
public int TotalLength { get; set; }
/// <summary>
/// Value of the cell
/// </summary>
public DbCellValue Value { get; set; }
/// <summary>
/// Constructs a new FileStreamReadResult
/// </summary>
/// <param name="value">The value of the result</param>
/// <param name="totalLength">The number of bytes for the used to store the value's length and value</param>
/// <param name="isNull">Whether or not the value is <c>null</c></param>
public FileStreamReadResult(T value, int totalLength, bool isNull)
/// <param name="value">The value of the result, ready for consumption by a client</param>
/// <param name="totalLength">The number of bytes for the used to store the value's length and value</param>s
public FileStreamReadResult(DbCellValue value, int totalLength)
{
Value = value;
TotalLength = totalLength;
IsNull = isNull;
}
}
}

View File

@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
@@ -15,21 +14,23 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
public interface IFileStreamReader : IDisposable
{
object[] ReadRow(long offset, IEnumerable<DbColumnWrapper> columns);
FileStreamReadResult<short> ReadInt16(long i64Offset);
FileStreamReadResult<int> ReadInt32(long i64Offset);
FileStreamReadResult<long> ReadInt64(long i64Offset);
FileStreamReadResult<byte> ReadByte(long i64Offset);
FileStreamReadResult<char> ReadChar(long i64Offset);
FileStreamReadResult<bool> ReadBoolean(long i64Offset);
FileStreamReadResult<float> ReadSingle(long i64Offset);
FileStreamReadResult<double> ReadDouble(long i64Offset);
FileStreamReadResult<SqlDecimal> ReadSqlDecimal(long i64Offset);
FileStreamReadResult<decimal> ReadDecimal(long i64Offset);
FileStreamReadResult<DateTime> ReadDateTime(long i64Offset);
FileStreamReadResult<TimeSpan> ReadTimeSpan(long i64Offset);
FileStreamReadResult<string> ReadString(long i64Offset);
FileStreamReadResult<byte[]> ReadBytes(long i64Offset);
FileStreamReadResult<DateTimeOffset> ReadDateTimeOffset(long i64Offset);
IList<DbCellValue> ReadRow(long offset, IEnumerable<DbColumnWrapper> columns);
FileStreamReadResult ReadInt16(long i64Offset);
FileStreamReadResult ReadInt32(long i64Offset);
FileStreamReadResult ReadInt64(long i64Offset);
FileStreamReadResult ReadByte(long i64Offset);
FileStreamReadResult ReadChar(long i64Offset);
FileStreamReadResult ReadBoolean(long i64Offset);
FileStreamReadResult ReadSingle(long i64Offset);
FileStreamReadResult ReadDouble(long i64Offset);
FileStreamReadResult ReadSqlDecimal(long i64Offset);
FileStreamReadResult ReadDecimal(long i64Offset);
FileStreamReadResult ReadDateTime(long i64Offset);
FileStreamReadResult ReadTimeSpan(long i64Offset);
FileStreamReadResult ReadString(long i64Offset);
FileStreamReadResult ReadBytes(long i64Offset);
FileStreamReadResult ReadDateTimeOffset(long i64Offset);
FileStreamReadResult ReadGuid(long offset);
FileStreamReadResult ReadMoney(long offset);
}
}

View File

@@ -29,7 +29,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int WriteDateTimeOffset(DateTimeOffset dtoVal);
int WriteTimeSpan(TimeSpan val);
int WriteString(string val);
int WriteBytes(byte[] bytes, int length);
int WriteBytes(byte[] bytes);
int WriteGuid(Guid val);
int WriteMoney(SqlMoney val);
void FlushBuffer();
}
}

View File

@@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.IO;
using System.Text;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
@@ -26,6 +25,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
private readonly IFileStreamWrapper fileStream;
private Dictionary<Type, Func<long, FileStreamReadResult>> readMethods;
#endregion
/// <summary>
@@ -41,6 +42,40 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// Create internal buffer
buffer = new byte[DefaultBufferSize];
// Create the methods that will be used to read back
readMethods = new Dictionary<Type, Func<long, FileStreamReadResult>>
{
{typeof(string), ReadString},
{typeof(short), ReadInt16},
{typeof(int), ReadInt32},
{typeof(long), ReadInt64},
{typeof(byte), ReadByte},
{typeof(char), ReadChar},
{typeof(bool), ReadBoolean},
{typeof(double), ReadDouble},
{typeof(float), ReadSingle},
{typeof(decimal), ReadDecimal},
{typeof(DateTime), ReadDateTime},
{typeof(DateTimeOffset), ReadDateTimeOffset},
{typeof(TimeSpan), ReadTimeSpan},
{typeof(byte[]), ReadBytes},
{typeof(SqlString), ReadString},
{typeof(SqlInt16), ReadInt16},
{typeof(SqlInt32), ReadInt32},
{typeof(SqlInt64), ReadInt64},
{typeof(SqlByte), ReadByte},
{typeof(SqlBoolean), ReadBoolean},
{typeof(SqlDouble), ReadDouble},
{typeof(SqlSingle), ReadSingle},
{typeof(SqlDecimal), ReadSqlDecimal},
{typeof(SqlDateTime), ReadDateTime},
{typeof(SqlBytes), ReadBytes},
{typeof(SqlBinary), ReadBytes},
{typeof(SqlGuid), ReadGuid},
{typeof(SqlMoney), ReadMoney},
};
}
#region IFileStreamStorage Implementation
@@ -50,12 +85,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file where the row starts</param>
/// <param name="columns">The columns that were encoded</param>
/// <returns>The objects from the row</returns>
public object[] ReadRow(long fileOffset, IEnumerable<DbColumnWrapper> columns)
/// <returns>The objects from the row, ready for output to the client</returns>
public IList<DbCellValue> ReadRow(long fileOffset, IEnumerable<DbColumnWrapper> columns)
{
// Initialize for the loop
long currentFileOffset = fileOffset;
List<object> results = new List<object>();
List<DbCellValue> results = new List<DbCellValue>();
// Iterate over the columns
foreach (DbColumnWrapper column in columns)
@@ -65,22 +100,23 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
if (column.IsSqlVariant)
{
// For SQL Variant columns, the type is written first in string format
FileStreamReadResult<string> sqlVariantTypeResult = ReadString(currentFileOffset);
FileStreamReadResult sqlVariantTypeResult = ReadString(currentFileOffset);
currentFileOffset += sqlVariantTypeResult.TotalLength;
string sqlVariantType = (string)sqlVariantTypeResult.Value.RawObject;
// If the typename is null, then the whole value is null
if (sqlVariantTypeResult.IsNull)
if (sqlVariantTypeResult.Value == null)
{
results.Add(null);
results.Add(sqlVariantTypeResult.Value);
continue;
}
// The typename is stored in the string
colType = Type.GetType(sqlVariantTypeResult.Value);
colType = Type.GetType(sqlVariantType);
// Workaround .NET bug, see sqlbu# 440643 and vswhidbey# 599834
// TODO: Is this workaround necessary for .NET Core?
if (colType == null && sqlVariantTypeResult.Value == @"System.Data.SqlTypes.SqlSingle")
if (colType == null && sqlVariantType == "System.Data.SqlTypes.SqlSingle")
{
colType = typeof(SqlSingle);
}
@@ -90,380 +126,19 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
colType = column.DataType;
}
if (colType == typeof(string))
{
// String - most frequently used data type
FileStreamReadResult<string> result = ReadString(currentFileOffset);
currentFileOffset += result.TotalLength;
results.Add(result.IsNull ? null : result.Value);
}
else if (colType == typeof(SqlString))
{
// SqlString
FileStreamReadResult<string> result = ReadString(currentFileOffset);
currentFileOffset += result.TotalLength;
results.Add(result.IsNull ? null : (SqlString) result.Value);
}
else if (colType == typeof(short))
{
// Int16
FileStreamReadResult<short> result = ReadInt16(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlInt16))
{
// SqlInt16
FileStreamReadResult<short> result = ReadInt16(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlInt16)result.Value);
}
}
else if (colType == typeof(int))
{
// Int32
FileStreamReadResult<int> result = ReadInt32(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlInt32))
{
// SqlInt32
FileStreamReadResult<int> result = ReadInt32(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlInt32)result.Value);
}
}
else if (colType == typeof(long))
{
// Int64
FileStreamReadResult<long> result = ReadInt64(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlInt64))
{
// SqlInt64
FileStreamReadResult<long> result = ReadInt64(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlInt64)result.Value);
}
}
else if (colType == typeof(byte))
{
// byte
FileStreamReadResult<byte> result = ReadByte(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlByte))
{
// SqlByte
FileStreamReadResult<byte> result = ReadByte(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlByte)result.Value);
}
}
else if (colType == typeof(char))
{
// Char
FileStreamReadResult<char> result = ReadChar(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(bool))
{
// Bool
FileStreamReadResult<bool> result = ReadBoolean(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlBoolean))
{
// SqlBoolean
FileStreamReadResult<bool> result = ReadBoolean(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlBoolean)result.Value);
}
}
else if (colType == typeof(double))
{
// double
FileStreamReadResult<double> result = ReadDouble(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlDouble))
{
// SqlByte
FileStreamReadResult<double> result = ReadDouble(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlDouble)result.Value);
}
}
else if (colType == typeof(float))
{
// float
FileStreamReadResult<float> result = ReadSingle(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlSingle))
{
// SqlSingle
FileStreamReadResult<float> result = ReadSingle(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlSingle)result.Value);
}
}
else if (colType == typeof(decimal))
{
// Decimal
FileStreamReadResult<decimal> result = ReadDecimal(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlDecimal))
{
// SqlDecimal
FileStreamReadResult<SqlDecimal> result = ReadSqlDecimal(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(DateTime))
{
// DateTime
FileStreamReadResult<DateTime> result = ReadDateTime(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlDateTime))
{
// SqlDateTime
FileStreamReadResult<DateTime> result = ReadDateTime(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add((SqlDateTime)result.Value);
}
}
else if (colType == typeof(DateTimeOffset))
{
// DateTimeOffset
FileStreamReadResult<DateTimeOffset> result = ReadDateTimeOffset(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(TimeSpan))
{
// TimeSpan
FileStreamReadResult<TimeSpan> result = ReadTimeSpan(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(byte[]))
{
// Byte Array
FileStreamReadResult<byte[]> result = ReadBytes(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull || (column.IsUdt && result.Value.Length == 0))
{
results.Add(null);
}
else
{
results.Add(result.Value);
}
}
else if (colType == typeof(SqlBytes))
{
// SqlBytes
FileStreamReadResult<byte[]> result = ReadBytes(currentFileOffset);
currentFileOffset += result.TotalLength;
results.Add(result.IsNull ? null : new SqlBytes(result.Value));
}
else if (colType == typeof(SqlBinary))
{
// SqlBinary
FileStreamReadResult<byte[]> result = ReadBytes(currentFileOffset);
currentFileOffset += result.TotalLength;
results.Add(result.IsNull ? null : new SqlBinary(result.Value));
}
else if (colType == typeof(SqlGuid))
{
// SqlGuid
FileStreamReadResult<byte[]> result = ReadBytes(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(new SqlGuid(result.Value));
}
}
else if (colType == typeof(SqlMoney))
{
// SqlMoney
FileStreamReadResult<decimal> result = ReadDecimal(currentFileOffset);
currentFileOffset += result.TotalLength;
if (result.IsNull)
{
results.Add(null);
}
else
{
results.Add(new SqlMoney(result.Value));
}
}
else
// Use the right read function for the type to read the data from the file
Func<long, FileStreamReadResult> readFunc;
if(!readMethods.TryGetValue(colType, out readFunc))
{
// Treat everything else as a string
FileStreamReadResult<string> result = ReadString(currentFileOffset);
currentFileOffset += result.TotalLength;
results.Add(result.IsNull ? null : result.Value);
}
readFunc = ReadString;
}
FileStreamReadResult result = readFunc(currentFileOffset);
currentFileOffset += result.TotalLength;
results.Add(result.Value);
}
return results.ToArray();
return results;
}
/// <summary>
@@ -471,21 +146,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the short from</param>
/// <returns>A short</returns>
public FileStreamReadResult<short> ReadInt16(long fileOffset)
public FileStreamReadResult ReadInt16(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 2, "Invalid data length");
bool isNull = length.ValueLength == 0;
short val = default(short);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = BitConverter.ToInt16(buffer, 0);
}
return new FileStreamReadResult<short>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => BitConverter.ToInt16(buffer, 0));
}
/// <summary>
@@ -493,19 +156,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the int from</param>
/// <returns>An int</returns>
public FileStreamReadResult<int> ReadInt32(long fileOffset)
public FileStreamReadResult ReadInt32(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 4, "Invalid data length");
bool isNull = length.ValueLength == 0;
int val = default(int);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = BitConverter.ToInt32(buffer, 0);
}
return new FileStreamReadResult<int>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => BitConverter.ToInt32(buffer, 0));
}
/// <summary>
@@ -513,19 +166,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the long from</param>
/// <returns>A long</returns>
public FileStreamReadResult<long> ReadInt64(long fileOffset)
public FileStreamReadResult ReadInt64(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 8, "Invalid data length");
bool isNull = length.ValueLength == 0;
long val = default(long);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = BitConverter.ToInt64(buffer, 0);
}
return new FileStreamReadResult<long>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => BitConverter.ToInt64(buffer, 0));
}
/// <summary>
@@ -533,19 +176,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the byte from</param>
/// <returns>A byte</returns>
public FileStreamReadResult<byte> ReadByte(long fileOffset)
public FileStreamReadResult ReadByte(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 1, "Invalid data length");
bool isNull = length.ValueLength == 0;
byte val = default(byte);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = buffer[0];
}
return new FileStreamReadResult<byte>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => buffer[0]);
}
/// <summary>
@@ -553,19 +186,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the char from</param>
/// <returns>A char</returns>
public FileStreamReadResult<char> ReadChar(long fileOffset)
public FileStreamReadResult ReadChar(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 2, "Invalid data length");
bool isNull = length.ValueLength == 0;
char val = default(char);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = BitConverter.ToChar(buffer, 0);
}
return new FileStreamReadResult<char>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => BitConverter.ToChar(buffer, 0));
}
/// <summary>
@@ -573,19 +196,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the bool from</param>
/// <returns>A bool</returns>
public FileStreamReadResult<bool> ReadBoolean(long fileOffset)
public FileStreamReadResult ReadBoolean(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 1, "Invalid data length");
bool isNull = length.ValueLength == 0;
bool val = default(bool);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = buffer[0] == 0x01;
}
return new FileStreamReadResult<bool>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => buffer[0] == 0x1);
}
/// <summary>
@@ -593,19 +206,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the single from</param>
/// <returns>A single</returns>
public FileStreamReadResult<float> ReadSingle(long fileOffset)
public FileStreamReadResult ReadSingle(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 4, "Invalid data length");
bool isNull = length.ValueLength == 0;
float val = default(float);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = BitConverter.ToSingle(buffer, 0);
}
return new FileStreamReadResult<float>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => BitConverter.ToSingle(buffer, 0));
}
/// <summary>
@@ -613,19 +216,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="fileOffset">Offset into the file to read the double from</param>
/// <returns>A double</returns>
public FileStreamReadResult<double> ReadDouble(long fileOffset)
public FileStreamReadResult ReadDouble(long fileOffset)
{
LengthResult length = ReadLength(fileOffset);
Debug.Assert(length.ValueLength == 0 || length.ValueLength == 8, "Invalid data length");
bool isNull = length.ValueLength == 0;
double val = default(double);
if (!isNull)
{
fileStream.ReadData(buffer, length.ValueLength);
val = BitConverter.ToDouble(buffer, 0);
}
return new FileStreamReadResult<double>(val, length.TotalLength, isNull);
return ReadCellHelper(fileOffset, length => BitConverter.ToDouble(buffer, 0));
}
/// <summary>
@@ -633,23 +226,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the SqlDecimal from</param>
/// <returns>A SqlDecimal</returns>
public FileStreamReadResult<SqlDecimal> ReadSqlDecimal(long offset)
public FileStreamReadResult ReadSqlDecimal(long offset)
{
LengthResult length = ReadLength(offset);
Debug.Assert(length.ValueLength == 0 || (length.ValueLength - 3)%4 == 0,
string.Format("Invalid data length: {0}", length.ValueLength));
bool isNull = length.ValueLength == 0;
SqlDecimal val = default(SqlDecimal);
if (!isNull)
return ReadCellHelper(offset, length =>
{
fileStream.ReadData(buffer, length.ValueLength);
int[] arrInt32 = new int[(length.ValueLength - 3)/4];
Buffer.BlockCopy(buffer, 3, arrInt32, 0, length.ValueLength - 3);
val = new SqlDecimal(buffer[0], buffer[1], 1 == buffer[2], arrInt32);
}
return new FileStreamReadResult<SqlDecimal>(val, length.TotalLength, isNull);
int[] arrInt32 = new int[(length - 3) / 4];
Buffer.BlockCopy(buffer, 3, arrInt32, 0, length - 3);
return new SqlDecimal(buffer[0], buffer[1], buffer[2] == 1, arrInt32);
});
}
/// <summary>
@@ -657,22 +241,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the decimal from</param>
/// <returns>A decimal</returns>
public FileStreamReadResult<decimal> ReadDecimal(long offset)
public FileStreamReadResult ReadDecimal(long offset)
{
LengthResult length = ReadLength(offset);
Debug.Assert(length.ValueLength%4 == 0, "Invalid data length");
bool isNull = length.ValueLength == 0;
decimal val = default(decimal);
if (!isNull)
return ReadCellHelper(offset, length =>
{
fileStream.ReadData(buffer, length.ValueLength);
int[] arrInt32 = new int[length.ValueLength/4];
Buffer.BlockCopy(buffer, 0, arrInt32, 0, length.ValueLength);
val = new decimal(arrInt32);
}
return new FileStreamReadResult<decimal>(val, length.TotalLength, isNull);
int[] arrInt32 = new int[length / 4];
Buffer.BlockCopy(buffer, 0, arrInt32, 0, length);
return new decimal(arrInt32);
});
}
/// <summary>
@@ -680,15 +256,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the DateTime from</param>
/// <returns>A DateTime</returns>
public FileStreamReadResult<DateTime> ReadDateTime(long offset)
public FileStreamReadResult ReadDateTime(long offset)
{
FileStreamReadResult<long> ticks = ReadInt64(offset);
DateTime val = default(DateTime);
if (!ticks.IsNull)
return ReadCellHelper(offset, length =>
{
val = new DateTime(ticks.Value);
}
return new FileStreamReadResult<DateTime>(val, ticks.TotalLength, ticks.IsNull);
long ticks = BitConverter.ToInt64(buffer, 0);
return new DateTime(ticks);
});
}
/// <summary>
@@ -696,27 +270,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the DateTimeOffset from</param>
/// <returns>A DateTimeOffset</returns>
public FileStreamReadResult<DateTimeOffset> ReadDateTimeOffset(long offset)
public FileStreamReadResult ReadDateTimeOffset(long offset)
{
// DateTimeOffset is represented by DateTime.Ticks followed by TimeSpan.Ticks
// both as Int64 values
// read the DateTime ticks
DateTimeOffset val = default(DateTimeOffset);
FileStreamReadResult<long> dateTimeTicks = ReadInt64(offset);
int totalLength = dateTimeTicks.TotalLength;
if (dateTimeTicks.TotalLength > 0 && !dateTimeTicks.IsNull)
{
// read the TimeSpan ticks
FileStreamReadResult<long> timeSpanTicks = ReadInt64(offset + dateTimeTicks.TotalLength);
Debug.Assert(!timeSpanTicks.IsNull, "TimeSpan ticks cannot be null if DateTime ticks are not null!");
totalLength += timeSpanTicks.TotalLength;
// build the DateTimeOffset
val = new DateTimeOffset(new DateTime(dateTimeTicks.Value), new TimeSpan(timeSpanTicks.Value));
}
return new FileStreamReadResult<DateTimeOffset>(val, totalLength, dateTimeTicks.IsNull);
return ReadCellHelper(offset, length => {
long dtTicks = BitConverter.ToInt64(buffer, 0);
long dtOffset = BitConverter.ToInt64(buffer, 8);
return new DateTimeOffset(new DateTime(dtTicks), new TimeSpan(dtOffset));
});
}
/// <summary>
@@ -724,15 +286,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the TimeSpan from</param>
/// <returns>A TimeSpan</returns>
public FileStreamReadResult<TimeSpan> ReadTimeSpan(long offset)
public FileStreamReadResult ReadTimeSpan(long offset)
{
FileStreamReadResult<long> timeSpanTicks = ReadInt64(offset);
TimeSpan val = default(TimeSpan);
if (!timeSpanTicks.IsNull)
return ReadCellHelper(offset, length =>
{
val = new TimeSpan(timeSpanTicks.Value);
}
return new FileStreamReadResult<TimeSpan>(val, timeSpanTicks.TotalLength, timeSpanTicks.IsNull);
long ticks = BitConverter.ToInt64(buffer, 0);
return new TimeSpan(ticks);
});
}
/// <summary>
@@ -740,24 +300,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the string from</param>
/// <returns>A string</returns>
public FileStreamReadResult<string> ReadString(long offset)
public FileStreamReadResult ReadString(long offset)
{
LengthResult fieldLength = ReadLength(offset);
Debug.Assert(fieldLength.ValueLength%2 == 0, "Invalid data length");
if (fieldLength.ValueLength == 0) // there is no data
{
// If the total length is 5 (5 bytes for length, 0 for value), then the string is empty
// Otherwise, the string is null
bool isNull = fieldLength.TotalLength != 5;
return new FileStreamReadResult<string>(isNull ? null : string.Empty,
fieldLength.TotalLength, isNull);
}
// positive length
AssureBufferLength(fieldLength.ValueLength);
fileStream.ReadData(buffer, fieldLength.ValueLength);
return new FileStreamReadResult<string>(Encoding.Unicode.GetString(buffer, 0, fieldLength.ValueLength), fieldLength.TotalLength, false);
return ReadCellHelper(offset, length =>
length > 0
? Encoding.Unicode.GetString(buffer, 0, length)
: string.Empty, totalLength => totalLength == 1);
}
/// <summary>
@@ -765,23 +313,54 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
/// <param name="offset">Offset into the file to read the bytes from</param>
/// <returns>A byte array</returns>
public FileStreamReadResult<byte[]> ReadBytes(long offset)
public FileStreamReadResult ReadBytes(long offset)
{
LengthResult fieldLength = ReadLength(offset);
if (fieldLength.ValueLength == 0)
return ReadCellHelper(offset, length =>
{
// If the total length is 5 (5 bytes for length, 0 for value), then the byte array is 0x
// Otherwise, the byte array is null
bool isNull = fieldLength.TotalLength != 5;
return new FileStreamReadResult<byte[]>(isNull ? null : new byte[0],
fieldLength.TotalLength, isNull);
}
byte[] output = new byte[length];
Buffer.BlockCopy(buffer, 0, output, 0, length);
return output;
}, totalLength => totalLength == 1,
bytes =>
{
StringBuilder sb = new StringBuilder("0x");
foreach (byte b in bytes)
{
sb.AppendFormat("{0:X2}", b);
}
return sb.ToString();
});
}
// positive length
byte[] val = new byte[fieldLength.ValueLength];
fileStream.ReadData(val, fieldLength.ValueLength);
return new FileStreamReadResult<byte[]>(val, fieldLength.TotalLength, false);
/// <summary>
/// Reads the bytes that make up a GUID at the offset provided
/// </summary>
/// <param name="offset">Offset into the file to read the bytes from</param>
/// <returns>A guid type object</returns>
public FileStreamReadResult ReadGuid(long offset)
{
return ReadCellHelper(offset, length =>
{
byte[] output = new byte[length];
Buffer.BlockCopy(buffer, 0, output, 0, length);
return new SqlGuid(output);
}, totalLength => totalLength == 1);
}
/// <summary>
/// Reads a SqlMoney type from the offset provided
/// into a
/// </summary>
/// <param name="offset"></param>
/// <returns>A sql money type object</returns>
public FileStreamReadResult ReadMoney(long offset)
{
return ReadCellHelper(offset, length =>
{
int[] arrInt32 = new int[length / 4];
Buffer.BlockCopy(buffer, 0, arrInt32, 0, length);
return new SqlMoney(new decimal(arrInt32));
});
}
/// <summary>
@@ -813,6 +392,58 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#endregion
#region Private Helpers
/// <summary>
/// Creates a new buffer that is of the specified length if the buffer is not already
/// at least as long as specified.
/// </summary>
/// <param name="newBufferLength">The minimum buffer size</param>
private void AssureBufferLength(int newBufferLength)
{
if (buffer.Length < newBufferLength)
{
buffer = new byte[newBufferLength];
}
}
/// <summary>
/// Reads the value of a cell from the file wrapper, checks to see if it null using
/// <paramref name="isNullFunc"/>, and converts it to the proper output type using
/// <paramref name="convertFunc"/>.
/// </summary>
/// <param name="offset">Offset into the file to read from</param>
/// <param name="convertFunc">Function to use to convert the buffer to the target type</param>
/// <param name="isNullFunc">
/// If provided, this function will be used to determine if the value is null
/// </param>
/// <param name="toStringFunc">Optional function to use to convert the object to a string.</param>
/// <typeparam name="T">The expected type of the cell. Used to keep the code honest</typeparam>
/// <returns>The object, a display value, and the length of the value + its length</returns>
private FileStreamReadResult ReadCellHelper<T>(long offset, Func<int, T> convertFunc, Func<int, bool> isNullFunc = null, Func<T, string> toStringFunc = null)
{
LengthResult length = ReadLength(offset);
DbCellValue result = new DbCellValue();
if (isNullFunc == null ? length.ValueLength == 0 : isNullFunc(length.TotalLength))
{
result.RawObject = null;
result.DisplayValue = null;
}
else
{
AssureBufferLength(length.ValueLength);
fileStream.ReadData(buffer, length.ValueLength);
T resultObject = convertFunc(length.ValueLength);
result.RawObject = resultObject;
result.DisplayValue = toStringFunc == null ? result.RawObject.ToString() : toStringFunc(resultObject);
}
return new FileStreamReadResult(result, length.TotalLength);
}
#endregion
/// <summary>
/// Internal struct used for representing the length of a field from the file
/// </summary>
@@ -837,19 +468,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
}
}
/// <summary>
/// Creates a new buffer that is of the specified length if the buffer is not already
/// at least as long as specified.
/// </summary>
/// <param name="newBufferLength">The minimum buffer size</param>
private void AssureBufferLength(int newBufferLength)
{
if (buffer.Length < newBufferLength)
{
buffer = new byte[newBufferLength];
}
}
#region IDisposable Implementation
private bool disposed;

View File

@@ -4,10 +4,10 @@
//
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
@@ -19,14 +19,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
public class ServiceBufferFileStreamWriter : IFileStreamWriter
{
#region Properties
private const int DefaultBufferLength = 8192;
public const int DefaultBufferLength = 8192;
private int MaxCharsToStore { get; set; }
private int MaxXmlCharsToStore { get; set; }
#region Member Variables
private readonly IFileStreamWrapper fileStream;
private readonly int maxCharsToStore;
private readonly int maxXmlCharsToStore;
private IFileStreamWrapper FileStream { get; set; }
private byte[] byteBuffer;
private readonly short[] shortBuffer;
private readonly int[] intBuffer;
@@ -35,6 +35,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
private readonly double[] doubleBuffer;
private readonly float[] floatBuffer;
/// <summary>
/// Functions to use for writing various types to a file
/// </summary>
private readonly Dictionary<Type, Func<object, int>> writeMethods;
#endregion
/// <summary>
@@ -47,8 +52,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
public ServiceBufferFileStreamWriter(IFileStreamWrapper fileWrapper, string fileName, int maxCharsToStore, int maxXmlCharsToStore)
{
// open file for reading/writing
FileStream = fileWrapper;
FileStream.Init(fileName, DefaultBufferLength, FileAccess.ReadWrite);
fileStream = fileWrapper;
fileStream.Init(fileName, DefaultBufferLength, FileAccess.ReadWrite);
// create internal buffer
byteBuffer = new byte[DefaultBufferLength];
@@ -63,8 +68,42 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
floatBuffer = new float[1];
// Store max chars to store
MaxCharsToStore = maxCharsToStore;
MaxXmlCharsToStore = maxXmlCharsToStore;
this.maxCharsToStore = maxCharsToStore;
this.maxXmlCharsToStore = maxXmlCharsToStore;
// Define what methods to use to write a type to the file
writeMethods = new Dictionary<Type, Func<object, int>>
{
{typeof(string), val => WriteString((string) val)},
{typeof(short), val => WriteInt16((short) val)},
{typeof(int), val => WriteInt32((int) val)},
{typeof(long), val => WriteInt64((long) val)},
{typeof(byte), val => WriteByte((byte) val)},
{typeof(char), val => WriteChar((char) val)},
{typeof(bool), val => WriteBoolean((bool) val)},
{typeof(double), val => WriteDouble((double) val) },
{typeof(float), val => WriteSingle((float) val) },
{typeof(decimal), val => WriteDecimal((decimal) val) },
{typeof(DateTime), val => WriteDateTime((DateTime) val) },
{typeof(DateTimeOffset), val => WriteDateTimeOffset((DateTimeOffset) val) },
{typeof(TimeSpan), val => WriteTimeSpan((TimeSpan) val) },
{typeof(byte[]), val => WriteBytes((byte[]) val)},
{typeof(SqlString), val => WriteNullable((SqlString) val, obj => WriteString((string) obj))},
{typeof(SqlInt16), val => WriteNullable((SqlInt16) val, obj => WriteInt16((short) obj))},
{typeof(SqlInt32), val => WriteNullable((SqlInt32) val, obj => WriteInt32((int) obj))},
{typeof(SqlInt64), val => WriteNullable((SqlInt64) val, obj => WriteInt64((long) obj)) },
{typeof(SqlByte), val => WriteNullable((SqlByte) val, obj => WriteByte((byte) obj)) },
{typeof(SqlBoolean), val => WriteNullable((SqlBoolean) val, obj => WriteBoolean((bool) obj)) },
{typeof(SqlDouble), val => WriteNullable((SqlDouble) val, obj => WriteDouble((double) obj)) },
{typeof(SqlSingle), val => WriteNullable((SqlSingle) val, obj => WriteSingle((float) obj)) },
{typeof(SqlDecimal), val => WriteNullable((SqlDecimal) val, obj => WriteSqlDecimal((SqlDecimal) obj)) },
{typeof(SqlDateTime), val => WriteNullable((SqlDateTime) val, obj => WriteDateTime((DateTime) obj)) },
{typeof(SqlBytes), val => WriteNullable((SqlBytes) val, obj => WriteBytes((byte[]) obj)) },
{typeof(SqlBinary), val => WriteNullable((SqlBinary) val, obj => WriteBytes((byte[]) obj)) },
{typeof(SqlGuid), val => WriteNullable((SqlGuid) val, obj => WriteGuid((Guid) obj)) },
{typeof(SqlMoney), val => WriteNullable((SqlMoney) val, obj => WriteMoney((SqlMoney) obj)) }
};
}
#region IFileStreamWriter Implementation
@@ -76,22 +115,20 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>Number of bytes used to write the row</returns>
public int WriteRow(StorageDataReader reader)
{
// Determine if we have any long fields
bool hasLongFields = reader.Columns.Any(column => column.IsLong);
// Read the values in from the db
object[] values = new object[reader.Columns.Length];
int rowBytes = 0;
if (!hasLongFields)
if (!reader.HasLongColumns)
{
// get all record values in one shot if there are no extra long fields
reader.GetValues(values);
}
// Loop over all the columns and write the values to the temp file
int rowBytes = 0;
for (int i = 0; i < reader.Columns.Length; i++)
{
DbColumnWrapper ci = reader.Columns[i];
if (hasLongFields)
if (reader.HasLongColumns)
{
if (reader.IsDBNull(i))
{
@@ -111,18 +148,18 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// this is a long field
if (ci.IsBytes)
{
values[i] = reader.GetBytesWithMaxCapacity(i, MaxCharsToStore);
values[i] = reader.GetBytesWithMaxCapacity(i, maxCharsToStore);
}
else if (ci.IsChars)
{
Debug.Assert(MaxCharsToStore > 0);
Debug.Assert(maxCharsToStore > 0);
values[i] = reader.GetCharsWithMaxCapacity(i,
ci.IsXml ? MaxXmlCharsToStore : MaxCharsToStore);
ci.IsXml ? maxXmlCharsToStore : maxCharsToStore);
}
else if (ci.IsXml)
{
Debug.Assert(MaxXmlCharsToStore > 0);
values[i] = reader.GetXmlWithMaxCapacity(i, MaxXmlCharsToStore);
Debug.Assert(maxXmlCharsToStore > 0);
values[i] = reader.GetXmlWithMaxCapacity(i, maxXmlCharsToStore);
}
else
{
@@ -133,8 +170,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
}
}
Type tVal = values[i].GetType(); // get true type of the object
// Get true type of the object
Type tVal = values[i].GetType();
// Write the object to a file
if (tVal == typeof(DBNull))
{
rowBytes += WriteNull();
@@ -148,272 +187,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
rowBytes += WriteString(val);
}
if (tVal == typeof(string))
// Use the appropriate writing method for the type
Func<object, int> writeMethod;
if (writeMethods.TryGetValue(tVal, out writeMethod))
{
// String - most frequently used data type
string val = (string)values[i];
rowBytes += WriteString(val);
}
else if (tVal == typeof(SqlString))
{
// SqlString
SqlString val = (SqlString)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteString(val.Value);
}
}
else if (tVal == typeof(short))
{
// Int16
short val = (short)values[i];
rowBytes += WriteInt16(val);
}
else if (tVal == typeof(SqlInt16))
{
// SqlInt16
SqlInt16 val = (SqlInt16)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteInt16(val.Value);
}
}
else if (tVal == typeof(int))
{
// Int32
int val = (int)values[i];
rowBytes += WriteInt32(val);
}
else if (tVal == typeof(SqlInt32))
{
// SqlInt32
SqlInt32 val = (SqlInt32)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteInt32(val.Value);
}
}
else if (tVal == typeof(long))
{
// Int64
long val = (long)values[i];
rowBytes += WriteInt64(val);
}
else if (tVal == typeof(SqlInt64))
{
// SqlInt64
SqlInt64 val = (SqlInt64)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteInt64(val.Value);
}
}
else if (tVal == typeof(byte))
{
// Byte
byte val = (byte)values[i];
rowBytes += WriteByte(val);
}
else if (tVal == typeof(SqlByte))
{
// SqlByte
SqlByte val = (SqlByte)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteByte(val.Value);
}
}
else if (tVal == typeof(char))
{
// Char
char val = (char)values[i];
rowBytes += WriteChar(val);
}
else if (tVal == typeof(bool))
{
// Boolean
bool val = (bool)values[i];
rowBytes += WriteBoolean(val);
}
else if (tVal == typeof(SqlBoolean))
{
// SqlBoolean
SqlBoolean val = (SqlBoolean)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteBoolean(val.Value);
}
}
else if (tVal == typeof(double))
{
// Double
double val = (double)values[i];
rowBytes += WriteDouble(val);
}
else if (tVal == typeof(SqlDouble))
{
// SqlDouble
SqlDouble val = (SqlDouble)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteDouble(val.Value);
}
}
else if (tVal == typeof(SqlSingle))
{
// SqlSingle
SqlSingle val = (SqlSingle)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteSingle(val.Value);
}
}
else if (tVal == typeof(decimal))
{
// Decimal
decimal val = (decimal)values[i];
rowBytes += WriteDecimal(val);
}
else if (tVal == typeof(SqlDecimal))
{
// SqlDecimal
SqlDecimal val = (SqlDecimal)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteSqlDecimal(val);
}
}
else if (tVal == typeof(DateTime))
{
// DateTime
DateTime val = (DateTime)values[i];
rowBytes += WriteDateTime(val);
}
else if (tVal == typeof(DateTimeOffset))
{
// DateTimeOffset
DateTimeOffset val = (DateTimeOffset)values[i];
rowBytes += WriteDateTimeOffset(val);
}
else if (tVal == typeof(SqlDateTime))
{
// SqlDateTime
SqlDateTime val = (SqlDateTime)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteDateTime(val.Value);
}
}
else if (tVal == typeof(TimeSpan))
{
// TimeSpan
TimeSpan val = (TimeSpan)values[i];
rowBytes += WriteTimeSpan(val);
}
else if (tVal == typeof(byte[]))
{
// Bytes
byte[] val = (byte[])values[i];
rowBytes += WriteBytes(val, val.Length);
}
else if (tVal == typeof(SqlBytes))
{
// SqlBytes
SqlBytes val = (SqlBytes)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteBytes(val.Value, val.Value.Length);
}
}
else if (tVal == typeof(SqlBinary))
{
// SqlBinary
SqlBinary val = (SqlBinary)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteBytes(val.Value, val.Value.Length);
}
}
else if (tVal == typeof(SqlGuid))
{
// SqlGuid
SqlGuid val = (SqlGuid)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
byte[] bytesVal = val.ToByteArray();
rowBytes += WriteBytes(bytesVal, bytesVal.Length);
}
}
else if (tVal == typeof(SqlMoney))
{
// SqlMoney
SqlMoney val = (SqlMoney)values[i];
if (val.IsNull)
{
rowBytes += WriteNull();
}
else
{
rowBytes += WriteDecimal(val.Value);
}
rowBytes += writeMethod(values[i]);
}
else
{
// treat everything else as string
string val = values[i].ToString();
rowBytes += WriteString(val);
rowBytes += WriteString(values[i].ToString());
}
}
}
@@ -430,7 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
public int WriteNull()
{
byteBuffer[0] = 0x00;
return FileStream.WriteData(byteBuffer, 1);
return fileStream.WriteData(byteBuffer, 1);
}
/// <summary>
@@ -442,7 +224,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x02; // length
shortBuffer[0] = val;
Buffer.BlockCopy(shortBuffer, 0, byteBuffer, 1, 2);
return FileStream.WriteData(byteBuffer, 3);
return fileStream.WriteData(byteBuffer, 3);
}
/// <summary>
@@ -454,7 +236,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x04; // length
intBuffer[0] = val;
Buffer.BlockCopy(intBuffer, 0, byteBuffer, 1, 4);
return FileStream.WriteData(byteBuffer, 5);
return fileStream.WriteData(byteBuffer, 5);
}
/// <summary>
@@ -466,7 +248,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x08; // length
longBuffer[0] = val;
Buffer.BlockCopy(longBuffer, 0, byteBuffer, 1, 8);
return FileStream.WriteData(byteBuffer, 9);
return fileStream.WriteData(byteBuffer, 9);
}
/// <summary>
@@ -478,7 +260,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x02; // length
charBuffer[0] = val;
Buffer.BlockCopy(charBuffer, 0, byteBuffer, 1, 2);
return FileStream.WriteData(byteBuffer, 3);
return fileStream.WriteData(byteBuffer, 3);
}
/// <summary>
@@ -489,7 +271,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
byteBuffer[0] = 0x01; // length
byteBuffer[1] = (byte) (val ? 0x01 : 0x00);
return FileStream.WriteData(byteBuffer, 2);
return fileStream.WriteData(byteBuffer, 2);
}
/// <summary>
@@ -500,7 +282,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
byteBuffer[0] = 0x01; // length
byteBuffer[1] = val;
return FileStream.WriteData(byteBuffer, 2);
return fileStream.WriteData(byteBuffer, 2);
}
/// <summary>
@@ -512,7 +294,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x04; // length
floatBuffer[0] = val;
Buffer.BlockCopy(floatBuffer, 0, byteBuffer, 1, 4);
return FileStream.WriteData(byteBuffer, 5);
return fileStream.WriteData(byteBuffer, 5);
}
/// <summary>
@@ -524,7 +306,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x08; // length
doubleBuffer[0] = val;
Buffer.BlockCopy(doubleBuffer, 0, byteBuffer, 1, 8);
return FileStream.WriteData(byteBuffer, 9);
return fileStream.WriteData(byteBuffer, 9);
}
/// <summary>
@@ -548,7 +330,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// data value
Buffer.BlockCopy(arrInt32, 0, byteBuffer, 3, iLen - 3);
iTotalLen += FileStream.WriteData(byteBuffer, iLen);
iTotalLen += fileStream.WriteData(byteBuffer, iLen);
return iTotalLen; // len+data
}
@@ -564,7 +346,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int iTotalLen = WriteLength(iLen); // length
Buffer.BlockCopy(arrInt32, 0, byteBuffer, 0, iLen);
iTotalLen += FileStream.WriteData(byteBuffer, iLen);
iTotalLen += fileStream.WriteData(byteBuffer, iLen);
return iTotalLen; // len+data
}
@@ -584,9 +366,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>Number of bytes used to store the DateTimeOffset</returns>
public int WriteDateTimeOffset(DateTimeOffset dtoVal)
{
// DateTimeOffset gets written as a DateTime + TimeOffset
// both represented as 'Ticks' written as Int64's
return WriteInt64(dtoVal.Ticks) + WriteInt64(dtoVal.Offset.Ticks);
// Write the length, which is the 2*sizeof(long)
byteBuffer[0] = 0x10; // length (16)
// Write the two longs, the datetime and the offset
long[] longBufferOffset = new long[2];
longBufferOffset[0] = dtoVal.Ticks;
longBufferOffset[1] = dtoVal.Offset.Ticks;
Buffer.BlockCopy(longBufferOffset, 0, byteBuffer, 1, 16);
return fileStream.WriteData(byteBuffer, 17);
}
/// <summary>
@@ -618,7 +406,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[3] = 0x00;
byteBuffer[4] = 0x00;
iTotalLen = FileStream.WriteData(byteBuffer, 5);
iTotalLen = fileStream.WriteData(byteBuffer, 5);
}
else
{
@@ -627,7 +415,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// convert char array into byte array and write it out
iTotalLen = WriteLength(bytes.Length);
iTotalLen += FileStream.WriteData(bytes, bytes.Length);
iTotalLen += fileStream.WriteData(bytes, bytes.Length);
}
return iTotalLen; // len+data
}
@@ -636,32 +424,76 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Writes a byte[] to the file
/// </summary>
/// <returns>Number of bytes used to store the byte[]</returns>
public int WriteBytes(byte[] bytesVal, int iLen)
public int WriteBytes(byte[] bytesVal)
{
Validate.IsNotNull(nameof(bytesVal), bytesVal);
int iTotalLen;
if (0 == iLen) // special case of 0 length byte array "0x"
if (bytesVal.Length == 0) // special case of 0 length byte array "0x"
{
iLen = 5;
AssureBufferLength(iLen);
AssureBufferLength(5);
byteBuffer[0] = 0xFF;
byteBuffer[1] = 0x00;
byteBuffer[2] = 0x00;
byteBuffer[3] = 0x00;
byteBuffer[4] = 0x00;
iTotalLen = FileStream.WriteData(byteBuffer, iLen);
iTotalLen = fileStream.WriteData(byteBuffer, 5);
}
else
{
iTotalLen = WriteLength(iLen);
iTotalLen += FileStream.WriteData(bytesVal, iLen);
iTotalLen = WriteLength(bytesVal.Length);
iTotalLen += fileStream.WriteData(bytesVal, bytesVal.Length);
}
return iTotalLen; // len+data
}
/// <summary>
/// Stores a GUID value to the file by treating it as a byte array
/// </summary>
/// <param name="val">The GUID to write to the file</param>
/// <returns>Number of bytes written to the file</returns>
public int WriteGuid(Guid val)
{
byte[] guidBytes = val.ToByteArray();
return WriteBytes(guidBytes);
}
/// <summary>
/// Stores a SqlMoney value to the file by treating it as a decimal
/// </summary>
/// <param name="val">The SqlMoney value to write to the file</param>
/// <returns>Number of bytes written to the file</returns>
public int WriteMoney(SqlMoney val)
{
return WriteDecimal(val.Value);
}
/// <summary>
/// Flushes the internal buffer to the file stream
/// </summary>
public void FlushBuffer()
{
fileStream.Flush();
}
#endregion
#region Private Helpers
/// <summary>
/// Creates a new buffer that is of the specified length if the buffer is not already
/// at least as long as specified.
/// </summary>
/// <param name="newBufferLength">The minimum buffer size</param>
private void AssureBufferLength(int newBufferLength)
{
if (newBufferLength > byteBuffer.Length)
{
byteBuffer = new byte[byteBuffer.Length];
}
}
/// <summary>
/// Writes the length of the field using the appropriate number of bytes (ie, 1 if the
/// length is &lt;255, 5 if the length is &gt;=255)
@@ -675,7 +507,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int iTmp = iLen & 0x000000FF;
byteBuffer[0] = Convert.ToByte(iTmp);
return FileStream.WriteData(byteBuffer, 1);
return fileStream.WriteData(byteBuffer, 1);
}
// The length won't fit in 1 byte, so we need to use 1 byte to signify that the length
// is a full 4 bytes.
@@ -684,27 +516,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// convert int32 into array of bytes
intBuffer[0] = iLen;
Buffer.BlockCopy(intBuffer, 0, byteBuffer, 1, 4);
return FileStream.WriteData(byteBuffer, 5);
return fileStream.WriteData(byteBuffer, 5);
}
/// <summary>
/// Flushes the internal buffer to the file stream
/// Writes a Nullable type (generally a Sql* type) to the file. The function provided by
/// <paramref name="valueWriteFunc"/> is used to write to the file if <paramref name="val"/>
/// is not null. <see cref="WriteNull"/> is used if <paramref name="val"/> is null.
/// </summary>
public void FlushBuffer()
/// <param name="val">The value to write to the file</param>
/// <param name="valueWriteFunc">The function to use if val is not null</param>
/// <returns>Number of bytes used to write value to the file</returns>
private int WriteNullable(INullable val, Func<object, int> valueWriteFunc)
{
FileStream.Flush();
return val.IsNull ? WriteNull() : valueWriteFunc(val);
}
#endregion
private void AssureBufferLength(int newBufferLength)
{
if (newBufferLength > byteBuffer.Length)
{
byteBuffer = new byte[byteBuffer.Length];
}
}
#region IDisposable Implementation
private bool disposed;
@@ -724,8 +553,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
if (disposing)
{
FileStream.Flush();
FileStream.Dispose();
fileStream.Flush();
fileStream.Dispose();
}
disposed = true;

View File

@@ -57,6 +57,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// Read the columns into a set of wrappers
Columns = DbDataReader.GetColumnSchema().Select(column => new DbColumnWrapper(column)).ToArray();
HasLongColumns = Columns.Any(column => column.IsLong);
}
#region Properties
@@ -71,6 +72,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// </summary>
public DbDataReader DbDataReader { get; private set; }
/// <summary>
/// Whether or not any of the columns of this reader are 'long', such as nvarchar(max)
/// </summary>
public bool HasLongColumns { get; private set; }
#endregion
#region DbDataReader Methods