Merge dev to master for 0.0.7 release (#86)

* Disable failing test while investigating

* Made connection errors more user-friendly (#57)

* Bug/negativeOneRowsAffected strings file fix (#61)

* Adding changes to sr.strings files

* Fixing bug by changing valid filename check to fail on whitespace (#55)

Fixing a bug from the unit tests on OSX/Unix where attempting to create a file with a name that's all whitespace succeeds when it should fail. This was passing in Windows because File.Open throws an ArgumentException when an all whitespace name is provided. In Unix systems, File.Open does not throw, causing the test to fail.

Solution is to check for whitespace in the sanity check.

* 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

* Feature/batch line info (#56)

* inital pipe of line numbers and getting text from workspace services

* tests compile

* Fixed bug regarding tests using connections on mac

* updated tests

* fixed workspace service and fixed tests

* integrated feedback

* Remove deleted file with whitespace name

* Feature/autocomp options (#63)

* Enable IntelliSense settings

* Fix up some bugs in the IntelliSense settings.

* Code cleans for PR

* Fix a couple exceptions that are breaks query execute and intellisense.

* Add useLowerCase flag and settings tests

* Remove task.wait from test to avoid break on build machine. (#67)

This is to fix test breaks so I'll merge now.  Please let me know if there are comments on this commit.

* Do not use ReliableCommand in the query execution service (#66)

* Do not use ReliableCommand in the query execution service.

* Fixing the logic to remove InfoMessage handlers from ReliableSqlConnection

* Adding test to query UDT

* Bump SMO to 140.1.6 to pick up perf fixes (#69)

* Enable Quick Info hover tooltips (#65)

Pushing to include in tomorrow's partner release build.  Please send me any feedback and I'll address in the next Intellisense PR.

* Added grouping between system/user dbs when listing (#70)

* Feature/timestamp messages (#68)

* added support for timestamps

* fixed tests

* Moved message class to own file; added 'z' to end of date strings

* added default time constructor

* removed unnecessary z

* added time string format info in comment

* changed from utc time to using local time

* Feature/save selection (#64)

* Save selection

* Add tests

* Change filename in test

* Code cleanup

* Refactor handler

* Code cleanup

* Modify tests to have query selection

* Change variable declaration

* Bump SMO to 14.0.7 to pick Batchparser updates (#72)

...bumping versions.  No review needed.

* Lingering File Handles (#71)

Fixing a bug where in various situations, the files used for temporary storage of query results would be leftover. In particular, the following changes were made:
* When the dispose query request is submitted, the corresponding query is now disposed in addition from being removed from the list of active queries
* When a query is cancelled, it is disposed after it is cancelled
* If a query already exists for a given ownerURI, the existing query is disposed before creating a new query
* All queries are disposed when the query execution service is disposed (ie, at shutdown of the service)

A unit test to verify the action of the dispose method for a ResultSet was added.

* Ensuring queries are disposed

Adding logic to dispose any queries when:
* URI that already has a query executes another query
* A request to dispose a query is submitted
* A request to cancel a query is submitted

* Small tweaks for cleanup of query execution service

* Add IntelliSense binding queue (#73)

* Initial code for binding queue

* Fix-up some of the timeout wait code

* Add some initial test code

* Add missing test file

* Update the binding queue tests

* Add more test coverage and refactor a bit.  Disable reliabile connection until we can fix it..it's holding an open data reader connection.

* A few more test updates

* Initial integrate queue with language service.

* Hook up the connected binding queue into al binding calls.

* Cleanup comments and remove dead code

* More missing comments

* Fix build break.  Reenable ReliabileConnection.

* Revert all changes to SqlConnectionFactory

* Resolve merge conflicts

* Cleanup some more of the timeouts and sync code

* Address code review feedback

* Address more code review feedback

* Feature/connect cancel (#74)

* Implemented connection cancellation

* Made connect requests return immediately and created a separate connection complete notification

* Fix spelling

* Fix sorting

* Add separate lock for cancellation source map

* Fix an issue with queue deadlocks causing test failures (#77)

* Fixed issue where connecting could take very long and cancellation would not work (#78)

* Fixed issue where connecting could take very long and cancellation would not work

* Addressing feedback

* Remove warning suppression

* Adding unlimited timeout for query execution (#76)

Adding explicitly setting the timeout for command execution to unlimited. We can change this to be user configurable at a later time

* Support 'for XML and for JSON' queries (#75)

* Set isXMl and isJson for 'for xml/json' resultSets

* Change string comparison

* Modify if-else

* VSTS 8499785. Close SqlToolsService after VS Code exits. (#80)

VSTS 8499785. Close SqlToolsService after VS Code exits.

* Remove extra level of tasks in binding queue (#79)

* Remove extra layer of tasks in binding queue

* Change order of assigning result to avoid race condition

* Add timeout log for the metadata lock event

* Fix test cases
This commit is contained in:
Karl Burtram
2016-10-08 19:18:26 +00:00
committed by GitHub
parent 7d6aabb618
commit 52b5f222db
65 changed files with 3739 additions and 1800 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

@@ -53,7 +53,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
// Sanity check for valid buffer length, fileName, and accessMethod
Validate.IsGreaterThan(nameof(bufferLength), bufferLength, 0);
Validate.IsNotNullOrEmptyString(nameof(fileName), fileName);
Validate.IsNotNullOrWhitespaceString(nameof(fileName), fileName);
if (accessMethod == FileAccess.Write)
{
throw new ArgumentException(SR.QueryServiceFileWrapperWriteOnly, nameof(fileName));

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 || string.IsNullOrEmpty(sqlVariantType))
{
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