mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-25 09:35:37 -05:00
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:
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// 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.EditData.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// A way to return a row in a result set that is being edited. It contains state about whether
|
||||
/// or not the row is dirty
|
||||
/// </summary>
|
||||
public class EditRow
|
||||
{
|
||||
public enum EditRowState
|
||||
{
|
||||
Clean = 0,
|
||||
DirtyInsert = 1,
|
||||
DirtyDelete = 2,
|
||||
DirtyUpdate = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The cells in the row. If the row has pending changes, they will be represented in
|
||||
/// this list
|
||||
/// </summary>
|
||||
public DbCellValue[] Cells { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal ID of the row. This should be used whenever referencing a row in row edit operations.
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the row has changes pending
|
||||
/// </summary>
|
||||
public bool IsDirty => State != EditRowState.Clean;
|
||||
|
||||
/// <summary>
|
||||
/// What type of dirty state (or lack thereof) the row is
|
||||
/// </summary>
|
||||
public EditRowState State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for a subset retrieval request
|
||||
/// </summary>
|
||||
public class EditSubsetParams : SessionOperationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Beginning index of the rows to return from the selected resultset. This index will be
|
||||
/// included in the results.
|
||||
/// </summary>
|
||||
public long RowStartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of rows to include in the result of this request. If the number of the rows
|
||||
/// exceeds the number of rows available after the start index, all available rows after
|
||||
/// the start index will be returned.
|
||||
/// </summary>
|
||||
public int RowCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters for the result of a subset retrieval request
|
||||
/// </summary>
|
||||
public class EditSubsetResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of rows returned from result set, useful for determining if less rows were
|
||||
/// returned than requested.
|
||||
/// </summary>
|
||||
public int RowCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The requested subset of rows, with information about whether or not the rows are dirty
|
||||
/// </summary>
|
||||
public EditRow[] Subset { get; set; }
|
||||
}
|
||||
|
||||
public class EditSubsetRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<EditSubsetParams, EditSubsetResult> Type =
|
||||
RequestType<EditSubsetParams, EditSubsetResult>.Create("edit/subset");
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
serviceHost.SetRequestHandler(EditInitializeRequest.Type, HandleInitializeRequest);
|
||||
serviceHost.SetRequestHandler(EditRevertCellRequest.Type, HandleRevertCellRequest);
|
||||
serviceHost.SetRequestHandler(EditRevertRowRequest.Type, HandleRevertRowRequest);
|
||||
serviceHost.SetRequestHandler(EditSubsetRequest.Type, HandleSubsetRequest);
|
||||
serviceHost.SetRequestHandler(EditUpdateCellRequest.Type, HandleUpdateCellRequest);
|
||||
}
|
||||
|
||||
@@ -241,6 +242,20 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
});
|
||||
}
|
||||
|
||||
internal Task HandleSubsetRequest(EditSubsetParams subsetParams,
|
||||
RequestContext<EditSubsetResult> requestContext)
|
||||
{
|
||||
return HandleSessionRequest(subsetParams, requestContext, session =>
|
||||
{
|
||||
EditRow[] rows = session.GetRows(subsetParams.RowStartIndex, subsetParams.RowCount).Result;
|
||||
return new EditSubsetResult
|
||||
{
|
||||
RowCount = rows.Length,
|
||||
Subset = rows
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
internal Task HandleUpdateCellRequest(EditUpdateCellParams updateParams,
|
||||
RequestContext<EditUpdateCellResult> requestContext)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
@@ -186,6 +187,60 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a subset of rows with the pending updates applied. If more rows than exist
|
||||
/// are requested, only the rows that exist will be returned.
|
||||
/// </summary>
|
||||
/// <param name="startIndex">Index to start returning rows from</param>
|
||||
/// <param name="rowCount">The number of rows to return.</param>
|
||||
/// <returns>An array of rows with pending edits applied</returns>
|
||||
public async Task<EditRow[]> GetRows(long startIndex, int rowCount)
|
||||
{
|
||||
// Get the cached rows from the result set
|
||||
ResultSetSubset cachedRows = startIndex < associatedResultSet.RowCount
|
||||
? await associatedResultSet.GetSubset(startIndex, rowCount)
|
||||
: new ResultSetSubset
|
||||
{
|
||||
RowCount = 0,
|
||||
Rows = new DbCellValue[][] { }
|
||||
};
|
||||
|
||||
// Convert the rows into EditRows and apply the changes we have
|
||||
List<EditRow> editRows = new List<EditRow>();
|
||||
for (int i = 0; i < cachedRows.RowCount; i++)
|
||||
{
|
||||
long rowId = i + startIndex;
|
||||
RowEditBase edr;
|
||||
if (EditCache.TryGetValue(rowId, out edr))
|
||||
{
|
||||
// Ask the edit object to generate an edit row
|
||||
editRows.Add(edr.GetEditRow(cachedRows.Rows[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Package up the existing row into a clean edit row
|
||||
EditRow er = new EditRow
|
||||
{
|
||||
Id = rowId,
|
||||
Cells = cachedRows.Rows[i],
|
||||
State = EditRow.EditRowState.Clean
|
||||
};
|
||||
editRows.Add(er);
|
||||
}
|
||||
}
|
||||
|
||||
// If the requested range of rows was at the end of the original cell set and we have
|
||||
// added new rows, we need to reflect those changes
|
||||
if (rowCount > cachedRows.RowCount)
|
||||
{
|
||||
long endIndex = startIndex + cachedRows.RowCount;
|
||||
var newRows = EditCache.Where(edit => edit.Key >= endIndex).Take(rowCount - cachedRows.RowCount);
|
||||
editRows.AddRange(newRows.Select(newRow => newRow.Value.GetEditRow(null)));
|
||||
}
|
||||
|
||||
return editRows.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverts a cell in a pending edit
|
||||
/// </summary>
|
||||
|
||||
@@ -85,6 +85,22 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Converts the cell update to a DbCellValue
|
||||
/// </summary>
|
||||
public DbCellValue AsDbCellValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DbCellValue
|
||||
{
|
||||
DisplayValue = ValueAsString,
|
||||
IsNull = Value == DBNull.Value,
|
||||
RawObject = Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The column that the cell will be placed in
|
||||
/// </summary>
|
||||
|
||||
@@ -122,6 +122,26 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a edit row that represents a row pending insertion
|
||||
/// </summary>
|
||||
/// <param name="cachedRow">Original, cached cell contents. (Should be null in this case)</param>
|
||||
/// <returns>EditRow of pending update</returns>
|
||||
public override EditRow GetEditRow(DbCellValue[] cachedRow)
|
||||
{
|
||||
// Iterate over the new cells. If they are null, generate a blank value
|
||||
DbCellValue[] editCells = newCells.Select(cell => cell == null
|
||||
? new DbCellValue {DisplayValue = string.Empty, IsNull = false, RawObject = null}
|
||||
: cell.AsDbCellValue)
|
||||
.ToArray();
|
||||
return new EditRow
|
||||
{
|
||||
Id = RowId,
|
||||
Cells = editCells,
|
||||
State = EditRow.EditRowState.DirtyInsert
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the INSERT INTO statement that will apply the row creation
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
@@ -72,6 +73,24 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a edit row that represents a row pending deletion. All the original cells are
|
||||
/// intact but the state is dirty.
|
||||
/// </summary>
|
||||
/// <param name="cachedRow">Original, cached cell contents</param>
|
||||
/// <returns>EditRow that is pending deletion</returns>
|
||||
public override EditRow GetEditRow(DbCellValue[] cachedRow)
|
||||
{
|
||||
Validate.IsNotNull(nameof(cachedRow), cachedRow);
|
||||
|
||||
return new EditRow
|
||||
{
|
||||
Id = RowId,
|
||||
Cells = cachedRow,
|
||||
State = EditRow.EditRowState.DirtyDelete
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a DELETE statement to delete this row
|
||||
/// </summary>
|
||||
|
||||
@@ -85,6 +85,14 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
/// <returns>Command to commit the change to the db</returns>
|
||||
public abstract DbCommand GetCommand(DbConnection connection);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a row that has the pending update applied. The dirty status of the row is
|
||||
/// reflected in the returned EditRow.
|
||||
/// </summary>
|
||||
/// <param name="cachedRow">The original, cached row values</param>
|
||||
/// <returns>An EditRow with the pending changes applied</returns>
|
||||
public abstract EditRow GetEditRow(DbCellValue[] cachedRow);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the row edit into a SQL statement
|
||||
/// </summary>
|
||||
|
||||
@@ -113,6 +113,30 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a edit row that represents a row with pending update. The cells pending
|
||||
/// updates are merged into the unchanged cells.
|
||||
/// </summary>
|
||||
/// <param name="cachedRow">Original, cached cell contents</param>
|
||||
/// <returns>EditRow with pending updates</returns>
|
||||
public override EditRow GetEditRow(DbCellValue[] cachedRow)
|
||||
{
|
||||
Validate.IsNotNull(nameof(cachedRow), cachedRow);
|
||||
|
||||
// For each cell that is pending update, replace the db cell value with a new one
|
||||
foreach (var cellUpdate in cellUpdates)
|
||||
{
|
||||
cachedRow[cellUpdate.Key] = cellUpdate.Value.AsDbCellValue;
|
||||
}
|
||||
|
||||
return new EditRow
|
||||
{
|
||||
Id = RowId,
|
||||
Cells = cachedRow,
|
||||
State = EditRow.EditRowState.DirtyUpdate
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an update statement to change the associated row.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user