mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-15 09:35:37 -05:00
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <255, 5 if the length is >=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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user