feature/edit/subset (#283)

* Changing query/subset API to only use Result on success, Error on error

* Creating an interservice API for getting query result subsets

* Updates to subset API

* RowStartIndex is now long
* Output of query/subset is a 2D array of DbCellValue
* Adding LongSkip method to LongList to allow skipping ahead by longs
* Moving LongList back to ServiceLayer utilities. Move refactoring

* Stubbing out request for edit/subset

* Initial implementation of getting edit rows

* Unit tests for RowEdit and RowDelete .GetEditRow

* Fixing major bugs in LongList implementation, adding much more thorough tests

* Adding some more unit tests and fixes to make unit tests pass

* Fixing comment
This commit is contained in:
Benjamin Russell
2017-03-21 15:14:04 -07:00
committed by GitHub
parent 9e576dea92
commit d7ecfb1a87
26 changed files with 1165 additions and 165 deletions

View File

@@ -355,7 +355,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 Task<ResultSetSubset> GetSubset(int resultSetIndex, int startRow, int rowCount)
public Task<ResultSetSubset> GetSubset(int resultSetIndex, long startRow, int rowCount)
{
ResultSet targetResultSet;
lock (resultSets)

View File

@@ -19,6 +19,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
/// <summary>
/// 2D array of the cell values requested from result set
/// </summary>
public string[][] Rows { get; set; }
public DbCellValue[][] Rows { get; set; }
}
}

View File

@@ -31,7 +31,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
/// Beginning index of the rows to return from the selected resultset. This index will be
/// included in the results.
/// </summary>
public int RowsStartIndex { get; set; }
public long RowsStartIndex { get; set; }
/// <summary>
/// Number of rows to include in the result of this request. If the number of the rows
@@ -46,11 +46,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
/// </summary>
public class SubsetResult
{
/// <summary>
/// Subset request error messages. Optional, can be set to null to indicate no errors
/// </summary>
public string Message { get; set; }
/// <summary>
/// The requested subset of results. Optional, can be set to null to indicate an error
/// </summary>

View File

@@ -280,7 +280,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 Task<ResultSetSubset> GetSubset(int batchIndex, int resultSetIndex, int startRow, int rowCount)
public Task<ResultSetSubset> GetSubset(int batchIndex, int resultSetIndex, long startRow, int rowCount)
{
// Sanity check to make sure that the batch is within bounds
if (batchIndex < 0 || batchIndex >= Batches.Length)

View File

@@ -176,42 +176,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
try
{
// Attempt to load the query
Query query;
if (!ActiveQueries.TryGetValue(subsetParams.OwnerUri, out query))
{
await requestContext.SendResult(new SubsetResult
{
Message = SR.QueryServiceRequestsNoQuery
});
return;
}
// Retrieve the requested subset and return it
ResultSetSubset subset = await InterServiceResultSubset(subsetParams);
var result = new SubsetResult
{
Message = null,
ResultSubset = await query.GetSubset(subsetParams.BatchIndex,
subsetParams.ResultSetIndex, subsetParams.RowsStartIndex, subsetParams.RowsCount)
ResultSubset = subset
};
await requestContext.SendResult(result);
}
catch (InvalidOperationException ioe)
{
// Return the error as a result
await requestContext.SendResult(new SubsetResult
{
Message = ioe.Message
});
}
catch (ArgumentOutOfRangeException aoore)
{
// Return the error as a result
await requestContext.SendResult(new SubsetResult
{
Message = aoore.Message
});
}
catch (Exception e)
{
// This was unexpected, so send back as error
@@ -415,7 +386,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <param name="ownerUri">The identifier of the query to be disposed</param>
/// <param name="successAction">Action to perform on success</param>
/// <param name="failureAction">Action to perform on failure</param>
/// <returns></returns>
public async Task InterServiceDisposeQuery(string ownerUri, Func<Task> successAction,
Func<string, Task> failureAction)
{
@@ -444,6 +414,29 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
}
/// <summary>
/// Retrieves the requested subset of rows from the requested result set. Intended to be
/// called by another service.
/// </summary>
/// <param name="subsetParams">Parameters for the subset to retrieve</param>
/// <returns>The requested subset</returns>
/// <exception cref="ArgumentOutOfRangeException">The requested query does not exist</exception>
public async Task<ResultSetSubset> InterServiceResultSubset(SubsetParams subsetParams)
{
Validate.IsNotNullOrEmptyString(nameof(subsetParams.OwnerUri), subsetParams.OwnerUri);
// Attempt to load the query
Query query;
if (!ActiveQueries.TryGetValue(subsetParams.OwnerUri, out query))
{
throw new ArgumentOutOfRangeException(SR.QueryServiceRequestsNoQuery);
}
// Retrieve the requested subset and return it
return await query.GetSubset(subsetParams.BatchIndex, subsetParams.ResultSetIndex,
subsetParams.RowsStartIndex, subsetParams.RowsCount);
}
#endregion
#region Private Helpers

View File

@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
@@ -223,7 +224,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 Task<ResultSetSubset> GetSubset(int startRow, int rowCount)
public Task<ResultSetSubset> GetSubset(long startRow, int rowCount)
{
// Sanity check to make sure that the results have been read beforehand
if (!hasBeenRead)
@@ -244,7 +245,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
return Task.Factory.StartNew(() =>
{
string[][] rows;
DbCellValue[][] rows;
using (IFileStreamReader fileStreamReader = fileStreamFactory.GetReader(outputFileName))
{
@@ -255,19 +256,23 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Iterate over all the rows and process them into a list of string builders
// ReSharper disable once AccessToDisposedClosure The lambda is used immediately in string.Join call
IEnumerable<string> rowValues = fileOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns)[0].DisplayValue);
rows = new[] { new[] { string.Join(string.Empty, rowValues) } };
string singleString = string.Join(string.Empty, rowValues);
DbCellValue cellValue = new DbCellValue
{
DisplayValue = singleString,
IsNull = false,
RawObject = singleString
};
rows = new[] { new[] { cellValue } };
}
else
{
// Figure out which rows we need to read back
IEnumerable<long> rowOffsets = fileOffsets.Skip(startRow).Take(rowCount);
IEnumerable<long> rowOffsets = fileOffsets.LongSkip(startRow).Take(rowCount);
// Iterate over the rows we need and process them into output
// ReSharper disable once AccessToDisposedClosure The lambda is used immediately in .ToArray call
rows = rowOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns)
.Select(cell => cell.DisplayValue).ToArray())
.ToArray();
rows = rowOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns).ToArray()).ToArray();
}
}
// Retrieve the subset of the results as per the request