mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-18 01:25:41 -05:00
* 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
337 lines
11 KiB
C#
337 lines
11 KiB
C#
//
|
|
// 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;
|
|
using System.Collections.Generic;
|
|
using Microsoft.SqlTools.Utility;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|
{
|
|
/// <summary>
|
|
/// Collection class that permits storage of over <c>int.MaxValue</c> items. This is performed
|
|
/// by using a 2D list of lists. The internal lists are only initialized as necessary. This
|
|
/// collection implements IEnumerable to make it easier to run LINQ queries against it.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This class is based on code from $\Data Tools\SSMS_Main\sql\ssms\core\DataStorage\ArrayList64.cs
|
|
/// with additions to bring it up to .NET 4.5 standards
|
|
/// </remarks>
|
|
/// <typeparam name="T">Type of the values to store</typeparam>
|
|
public class LongList<T> : IEnumerable<T>
|
|
{
|
|
#region Member Variables
|
|
|
|
private List<List<T>> expandedList;
|
|
private readonly List<T> shortList;
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Creates a new long list
|
|
/// </summary>
|
|
public LongList()
|
|
{
|
|
shortList = new List<T>();
|
|
ExpandListSize = int.MaxValue;
|
|
Count = 0;
|
|
}
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// The total number of elements in the array
|
|
/// </summary>
|
|
public long Count { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Used to get or set the value at a given index in the list
|
|
/// </summary>
|
|
/// <param name="index">Index into the list to access</param>
|
|
public T this[long index]
|
|
{
|
|
get
|
|
{
|
|
return GetItem(index);
|
|
}
|
|
|
|
set
|
|
{
|
|
SetItem(index, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The number of elements to store in a single list before expanding into multiple lists
|
|
/// </summary>
|
|
public int ExpandListSize { get; internal set; }
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Adds the specified value to the end of the list
|
|
/// </summary>
|
|
/// <param name="val">Value to add to the list</param>
|
|
/// <returns>Index of the item that was just added</returns>
|
|
public long Add(T val)
|
|
{
|
|
if (Count < this.ExpandListSize)
|
|
{
|
|
shortList.Add(val);
|
|
}
|
|
else // need to split values into several arrays
|
|
{
|
|
if (expandedList == null)
|
|
{
|
|
// very inefficient so delay as much as possible
|
|
// immediately add 0th array
|
|
expandedList = new List<List<T>> {shortList};
|
|
}
|
|
|
|
int arrayIndex = (int)(Count / this.ExpandListSize); // 0 based
|
|
|
|
List<T> arr;
|
|
if (expandedList.Count <= arrayIndex) // need to make a new array
|
|
{
|
|
arr = new List<T>();
|
|
expandedList.Add(arr);
|
|
}
|
|
else // use existing array
|
|
{
|
|
arr = expandedList[arrayIndex];
|
|
}
|
|
arr.Add(val);
|
|
}
|
|
return (++Count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the item at the specified index
|
|
/// </summary>
|
|
/// <param name="index">Index of the item to return</param>
|
|
/// <returns>The item at the index specified</returns>
|
|
public T GetItem(long index)
|
|
{
|
|
Validate.IsWithinRange(nameof(index), index, 0, Count - 1);
|
|
|
|
T val = default(T);
|
|
if (Count < this.ExpandListSize)
|
|
{
|
|
int i32Index = Convert.ToInt32(index);
|
|
val = shortList[i32Index];
|
|
}
|
|
else
|
|
{
|
|
int iArray32Index = (int) (index / this.ExpandListSize);
|
|
if (expandedList.Count > iArray32Index)
|
|
{
|
|
List<T> arr = expandedList[iArray32Index];
|
|
|
|
int i32Index = (int) (index % this.ExpandListSize);
|
|
if (arr.Count > i32Index)
|
|
{
|
|
val = arr[i32Index];
|
|
}
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Skips ahead the number of elements requested and returns the elements after that many elements
|
|
/// </summary>
|
|
/// <param name="start">The number of elements to skip</param>
|
|
/// <returns>All elements after the number of elements to skip</returns>
|
|
public IEnumerable<T> LongSkip(long start)
|
|
{
|
|
Validate.IsWithinRange(nameof(start), start, 0, Count - 1);
|
|
|
|
// Generate an enumerator over this list and jump ahead to the position we want
|
|
LongListEnumerator<T> longEnumerator = new LongListEnumerator<T>(this) {Index = start - 1};
|
|
|
|
// While there are results to get, yield return them
|
|
while (longEnumerator.MoveNext())
|
|
{
|
|
yield return longEnumerator.Current;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the item at the specified index
|
|
/// </summary>
|
|
/// <param name="index">Index of the item to set</param>
|
|
/// <param name="value">The item to store at the index specified</param>
|
|
public void SetItem(long index, T value)
|
|
{
|
|
Validate.IsLessThan(nameof(index), index, Count);
|
|
|
|
if (Count <= this.ExpandListSize)
|
|
{
|
|
int i32Index = Convert.ToInt32(index);
|
|
shortList[i32Index] = value;
|
|
}
|
|
else
|
|
{
|
|
int iArray32Index = (int) (index / this.ExpandListSize);
|
|
List<T> arr = expandedList[iArray32Index];
|
|
|
|
int i32Index = (int)(index % this.ExpandListSize);
|
|
arr[i32Index] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an item at the specified location and shifts all the items after the provided
|
|
/// index up by one.
|
|
/// </summary>
|
|
/// <param name="index">The index to remove from the list</param>
|
|
public void RemoveAt(long index)
|
|
{
|
|
Validate.IsWithinRange(nameof(index), index, 0, Count - 1);
|
|
|
|
if (Count <= this.ExpandListSize)
|
|
{
|
|
int iArray32MemberIndex = Convert.ToInt32(index); // 0 based
|
|
shortList.RemoveAt(iArray32MemberIndex);
|
|
}
|
|
else // handle the case of multiple arrays
|
|
{
|
|
// find out which array it is in
|
|
int arrayIndex = (int) (index / this.ExpandListSize);
|
|
List<T> arr = expandedList[arrayIndex];
|
|
|
|
// find out index into this array
|
|
int iArray32MemberIndex = (int) (index % this.ExpandListSize);
|
|
arr.RemoveAt(iArray32MemberIndex);
|
|
|
|
// now shift members of the array back one
|
|
//int iArray32TotalIndex = (int) (Count / this.ExpandListSize);
|
|
for (int i = arrayIndex + 1; i < expandedList.Count; i++)
|
|
{
|
|
List<T> arr1 = expandedList[i - 1];
|
|
List<T> arr2 = expandedList[i];
|
|
|
|
arr1.Add(arr2[0]);
|
|
arr2.RemoveAt(0);
|
|
|
|
if (arr2.Count == 0)
|
|
{
|
|
expandedList.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
--Count;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<object> Implementation
|
|
|
|
/// <summary>
|
|
/// Returns a generic enumerator for enumeration of this LongList
|
|
/// </summary>
|
|
/// <returns>Enumerator for LongList</returns>
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return new LongListEnumerator<T>(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an enumerator for enumeration of this LongList
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
|
|
public class LongListEnumerator<TEt> : IEnumerator<TEt>
|
|
{
|
|
/// <summary>
|
|
/// The current list that we're iterating over.
|
|
/// </summary>
|
|
private readonly LongList<TEt> localList;
|
|
|
|
/// <summary>
|
|
/// The current index into the list
|
|
/// </summary>
|
|
private long index;
|
|
|
|
/// <summary>
|
|
/// Constructs a new enumerator for a given LongList
|
|
/// </summary>
|
|
/// <param name="list">The list to enumerate</param>
|
|
public LongListEnumerator(LongList<TEt> list)
|
|
{
|
|
localList = list;
|
|
index = -1;
|
|
Current = default(TEt);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The index into the list of the item that is the current item. Upon setting,
|
|
/// <see cref="Current"/> will be updated if the index is in range. Otherwise,
|
|
/// <c>default(<see cref="TEt"/>)</c> will be used.
|
|
/// </summary>
|
|
public long Index
|
|
{
|
|
get { return index; }
|
|
set
|
|
{
|
|
index = value;
|
|
Current = value >= localList.Count || value < 0
|
|
? default(TEt)
|
|
: localList[index];
|
|
}
|
|
}
|
|
|
|
#region IEnumerator Implementation
|
|
|
|
/// <summary>
|
|
/// Returns the current item in the enumeration
|
|
/// </summary>
|
|
public TEt Current { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Returns the current item in the enumeration
|
|
/// </summary>
|
|
object IEnumerator.Current => Current;
|
|
|
|
/// <summary>
|
|
/// Moves to the next item in the list we're iterating over
|
|
/// </summary>
|
|
/// <returns>Whether or not the move was successful</returns>
|
|
public bool MoveNext()
|
|
{
|
|
Index++;
|
|
return Index < localList.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the enumeration
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
Index = 0;
|
|
Current = default(TEt);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposal method. Does nothing.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
}
|
|
|