Feature: Writing Execute Results to Temp File (#35)

* WIP for buffering in temporary file

* Adding support for writing to disk for buffering

* WIP - Adding file reader, factory for reader/writer

* Making long list use generics and implement IEnumerable

* Reading/Writing from file is working

* Removing unused 'skipValue' logic

* More tweaks to file buffer

Adding logic for cleaning up the temp files
Adding fix for empty/null column names

* Adding comments and cleanup

* Unit tests for FileStreamWrapper

* WIP adding more unit tests, and finishing up wiring up the output writers

* Finishing up initial unit tests

* Fixing bugs with long fields

* Squashed commit of the following:

commit df0ffc12a46cb286d801d08689964eac08ad71dd
Author: Benjamin Russell <beruss@microsoft.com>
Date:   Wed Sep 7 14:45:39 2016 -0700

    Removing last bit of async for file writing.

    We're seeing a 8x improvement of file write speeds!

commit 08a4b9f32e825512ca24d5dc03ef5acbf7cc6d94
Author: Benjamin Russell <beruss@microsoft.com>
Date:   Wed Sep 7 11:23:06 2016 -0700

    Removing async wrappers

* Rolling back test code for Program.cs

* Changes as per code review

* Fixing broken unit tests

* More fixes for codereview
This commit is contained in:
Benjamin Russell
2016-09-08 17:55:11 -07:00
committed by GitHub
parent 903eab61d1
commit 8aa3d524fc
24 changed files with 4050 additions and 195 deletions

View File

@@ -1,7 +1,6 @@
//
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
@@ -11,18 +10,60 @@ using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
/// <summary>
/// This class represents a batch within a query
/// </summary>
public class Batch
public class Batch : IDisposable
{
private const string RowsAffectedFormat = "({0} row(s) affected)";
#region Member Variables
/// <summary>
/// For IDisposable implementation, whether or not this has been disposed
/// </summary>
private bool disposed;
/// <summary>
/// Factory for creating readers/writrs for the output of the batch
/// </summary>
private readonly IFileStreamFactory outputFileFactory;
/// <summary>
/// Internal representation of the messages so we can modify internally
/// </summary>
private readonly List<string> resultMessages;
/// <summary>
/// Internal representation of the result sets so we can modify internally
/// </summary>
private readonly List<ResultSet> resultSets;
#endregion
internal Batch(string batchText, int startLine, IFileStreamFactory outputFileFactory)
{
// Sanity check for input
Validate.IsNotNullOrEmptyString(nameof(batchText), batchText);
Validate.IsNotNull(nameof(outputFileFactory), outputFileFactory);
// Initialize the internal state
BatchText = batchText;
StartLine = startLine - 1; // -1 to make sure that the line number of the batch is 0-indexed, since SqlParser gives 1-indexed line numbers
HasExecuted = false;
resultSets = new List<ResultSet>();
resultMessages = new List<string>();
this.outputFileFactory = outputFileFactory;
}
#region Properties
/// <summary>
/// The text of batch that will be executed
/// </summary>
@@ -38,11 +79,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// </summary>
public bool HasExecuted { get; set; }
/// <summary>
/// Internal representation of the messages so we can modify internally
/// </summary>
private List<string> resultMessages;
/// <summary>
/// Messages that have come back from the server
/// </summary>
@@ -51,11 +87,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
get { return resultMessages; }
}
/// <summary>
/// Internal representation of the result sets so we can modify internally
/// </summary>
private List<ResultSet> resultSets;
/// <summary>
/// The result sets of the batch execution
/// </summary>
@@ -75,7 +106,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
ColumnInfo = set.Columns,
Id = index,
RowCount = set.Rows.Count
RowCount = set.RowCount
}).ToArray();
}
}
@@ -87,21 +118,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
#endregion
public Batch(string batchText, int startLine)
{
// Sanity check for input
if (string.IsNullOrEmpty(batchText))
{
throw new ArgumentNullException(nameof(batchText), "Query text cannot be null");
}
// Initialize the internal state
BatchText = batchText;
StartLine = startLine - 1; // -1 to make sure that the line number of the batch is 0-indexed, since SqlParser gives 1-indexed line numbers
HasExecuted = false;
resultSets = new List<ResultSet>();
resultMessages = new List<string>();
}
#region Public Methods
/// <summary>
/// Executes this batch and captures any server messages that are returned.
@@ -148,23 +165,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
// Read until we hit the end of the result set
ResultSet resultSet = new ResultSet();
while (await reader.ReadAsync(cancellationToken))
{
resultSet.AddRow(reader);
}
// Read off the column schema information
if (reader.CanGetColumnSchema())
{
resultSet.Columns = reader.GetColumnSchema().ToArray();
}
ResultSet resultSet = new ResultSet(reader, outputFileFactory);
await resultSet.ReadResultToEnd(cancellationToken);
// Add the result set to the results of the query
resultSets.Add(resultSet);
// Add a message for the number of rows the query returned
resultMessages.Add(string.Format(RowsAffectedFormat, resultSet.Rows.Count));
resultMessages.Add(string.Format(RowsAffectedFormat, resultSet.RowCount));
} while (await reader.NextResultAsync(cancellationToken));
}
}
@@ -200,7 +208,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <param name="startRow">The starting row of the results</param>
/// <param name="rowCount">How many rows to retrieve</param>
/// <returns>A subset of results</returns>
public ResultSetSubset GetSubset(int resultSetIndex, int startRow, int rowCount)
public Task<ResultSetSubset> GetSubset(int resultSetIndex, int startRow, int rowCount)
{
// Sanity check to make sure we have valid numbers
if (resultSetIndex < 0 || resultSetIndex >= resultSets.Count)
@@ -213,6 +221,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
return resultSets[resultSetIndex].GetSubset(startRow, rowCount);
}
#endregion
#region Private Helpers
/// <summary>
@@ -259,5 +269,33 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
#endregion
#region IDisposable Implementation
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
foreach (ResultSet r in ResultSets)
{
r.Dispose();
}
}
disposed = true;
}
#endregion
}
}