diff --git a/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs b/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs
index 23d53ba1c6..193c172209 100644
--- a/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs
+++ b/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs
@@ -2,6 +2,7 @@
namespace System.Data.Entity.Core.Query.InternalTrees
{
+ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
@@ -9,25 +10,21 @@ namespace System.Data.Entity.Core.Query.InternalTrees
//
// Helps map one variable to the next.
//
- internal class VarMap : Dictionary
+ internal class VarMap : IDictionary
{
#region public surfaces
+ private Dictionary map;
+ private Dictionary reverseMap;
+
internal VarMap GetReverseMap()
{
- var reverseMap = new VarMap();
- foreach (var kv in this)
- {
- Var x;
- // On the odd chance that a var is in the varMap more than once, the first one
- // is going to be the one we want to use, because it might be the discriminator
- // var;
- if (!reverseMap.TryGetValue(kv.Value, out x))
- {
- reverseMap[kv.Value] = kv.Key;
- }
- }
- return reverseMap;
+ return new VarMap(reverseMap, map);
+ }
+
+ public bool ContainsValue(Var value)
+ {
+ return reverseMap.ContainsKey(value);
}
public override string ToString()
@@ -35,7 +32,7 @@ public override string ToString()
var sb = new StringBuilder();
var separator = string.Empty;
- foreach (var v in Keys)
+ foreach (var v in map.Keys)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", separator, v.Id, this[v].Id);
separator = ",";
@@ -45,8 +42,134 @@ public override string ToString()
#endregion
+ #region IDictionary
+
+ public Var this[Var key]
+ {
+ get
+ {
+ return map[key];
+ }
+ set
+ {
+ map[key] = value;
+ }
+ }
+
+ public ICollection Keys
+ {
+ get
+ {
+ return map.Keys;
+ }
+ }
+
+ public ICollection Values
+ {
+ get
+ {
+ return map.Values;
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return map.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(Var key, Var value)
+ {
+ if (!reverseMap.ContainsKey(value))
+ {
+ reverseMap.Add(value, key);
+ }
+ map.Add(key, value);
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ if (!reverseMap.ContainsKey(item.Value))
+ {
+ ((IDictionary)reverseMap).Add(new KeyValuePair(item.Value, item.Key));
+ }
+ ((IDictionary)map).Add(item);
+ }
+
+ public void Clear()
+ {
+ map.Clear();
+ reverseMap.Clear();
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return ((IDictionary)map).Contains(item);
+ }
+
+ public bool ContainsKey(Var key)
+ {
+ return map.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ ((IDictionary)map).CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return map.GetEnumerator();
+ }
+
+ public bool Remove(Var key)
+ {
+ reverseMap.Remove(map[key]);
+ return map.Remove(key);
+ }
+
+ public bool Remove(KeyValuePair item)
+ {
+ reverseMap.Remove(map[item.Value]);
+ return ((IDictionary)map).Remove(item);
+ }
+
+ public bool TryGetValue(Var key, out Var value)
+ {
+ return ((IDictionary)map).TryGetValue(key, out value);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return map.GetEnumerator();
+ }
+
+ #endregion
+
#region constructors
+ public VarMap()
+ {
+ map = new Dictionary();
+ reverseMap = new Dictionary();
+ }
+
+ private VarMap(Dictionary map, Dictionary reverseMap)
+ {
+ this.map = map;
+ this.reverseMap = reverseMap;
+ }
+
#endregion
}
}
diff --git a/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs b/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs
index 427341c5d6..07a9d2c97a 100644
--- a/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs
+++ b/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs
@@ -3,6 +3,7 @@
namespace System.Data.Entity.Core.Query.InternalTrees
{
using System.Collections;
+ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
@@ -28,7 +29,7 @@ internal class VarVecEnumerator : IEnumerator, IDisposable
private int m_position;
private Command m_command;
- private BitArray m_bitArray;
+ private BitVec m_bitArray;
#endregion
@@ -77,19 +78,54 @@ object IEnumerator.Current
get { return Current; }
}
+ static readonly int[] MultiplyDeBruijnBitPosition =
+ {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+
//
// Move to the next position
//
public bool MoveNext()
{
+ int[] values = m_bitArray.m_array;
m_position++;
- for (; m_position < m_bitArray.Length; m_position++)
+ int length = m_bitArray.Length;
+ int valuesLen = BitVec.GetArrayLength(length, 32);
+ int i = m_position / 32;
+ int v = 0, mask = 0;
+
+ if (i < valuesLen)
{
- if (m_bitArray[m_position])
+
+ v = values[i];
+ // zero lowest bits that are skipped
+ mask = (~0 << (m_position % 32));
+
+ v &= mask;
+
+ if (v != 0)
{
+ m_position = (i * 32) + MultiplyDeBruijnBitPosition[((uint)((v & -v) * 0x077CB531U)) >> 27];
+ return true;
+ }
+
+ i++;
+ for (; i < valuesLen; i++)
+ {
+ v = values[i];
+
+ if (v == 0)
+ {
+ continue;
+ }
+
+ m_position = (i * 32) + MultiplyDeBruijnBitPosition[((uint)((v & -v) * 0x077CB531U)) >> 27];
return true;
}
}
+ m_position = length;
return false;
}
@@ -174,10 +210,26 @@ internal bool Overlaps(VarVec other)
//
internal bool Subsumes(VarVec other)
{
- for (var i = 0; i < other.m_bitVector.Length; i++)
+ int[] values = m_bitVector.m_array;
+ int[] otherValues = other.m_bitVector.m_array;
+
+ // if the other is longer, and it has a bit set past the current vector's length return false
+ if (otherValues.Length > values.Length)
{
- if (other.m_bitVector[i]
- && ((i >= m_bitVector.Length) || !m_bitVector[i]))
+ for (var i = values.Length; i < otherValues.Length; i++)
+ {
+ if (otherValues[i] != 0)
+ {
+ return false;
+ }
+ }
+ }
+
+ int length = Math.Min(otherValues.Length, values.Length);
+
+ for (var i = 0; i < length; i++)
+ {
+ if (!((values[i] & otherValues[i]) == otherValues[i]))
{
return false;
}
@@ -287,7 +339,7 @@ internal Var First
//
// dictionary of renamed vars
// a new VarVec
- internal VarVec Remap(Dictionary varMap)
+ internal VarVec Remap(IDictionary varMap)
{
var newVec = m_command.CreateVarVec();
foreach (var v in this)
@@ -308,7 +360,7 @@ internal VarVec Remap(Dictionary varMap)
internal VarVec(Command command)
{
- m_bitVector = new BitArray(64);
+ m_bitVector = new BitVec(64);
m_command = command;
}
@@ -361,7 +413,7 @@ public override string ToString()
#region private state
- private readonly BitArray m_bitVector;
+ private readonly BitVec m_bitVector;
private readonly Command m_command;
#endregion
@@ -380,4 +432,465 @@ public VarVec Clone()
#endregion
}
+
+ internal class BitVec
+ {
+ private BitVec()
+ {
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold length bit values. All of the values in the bit
+ ** array are set to false.
+ **
+ ** Exceptions: ArgumentException if length < 0.
+ =========================================================================*/
+ public BitVec(int length)
+ : this(length, false)
+ {
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold length bit values. All of the values in the bit
+ ** array are set to defaultValue.
+ **
+ ** Exceptions: ArgumentOutOfRangeException if length < 0.
+ =========================================================================*/
+ public BitVec(int length, bool defaultValue)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NeedNonNegNum");
+ }
+
+ m_array = ArrayPool.Instance.GetArray(GetArrayLength(length, BitsPerInt32));
+ m_length = length;
+
+ int fillValue = defaultValue ? unchecked(((int)0xffffffff)) : 0;
+ for (int i = 0; i < m_array.Length; i++)
+ {
+ m_array[i] = fillValue;
+ }
+
+ _version = 0;
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold the bit values in bytes. bytes[0] represents
+ ** bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte
+ ** represents the lowest index value; bytes[0] & 1 represents bit 0,
+ ** bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc.
+ **
+ ** Exceptions: ArgumentException if bytes == null.
+ =========================================================================*/
+ public BitVec(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes");
+ }
+
+ // this value is chosen to prevent overflow when computing m_length.
+ // m_length is of type int32 and is exposed as a property, so
+ // type of m_length can't be changed to accommodate.
+ if (bytes.Length > Int32.MaxValue / BitsPerByte)
+ {
+ throw new ArgumentException("Argument_ArrayTooLarge", "bytes");
+ }
+
+ m_array = ArrayPool.Instance.GetArray(GetArrayLength(bytes.Length, BytesPerInt32));
+ m_length = bytes.Length * BitsPerByte;
+
+ int i = 0;
+ int j = 0;
+ while (bytes.Length - j >= 4)
+ {
+ m_array[i++] = (bytes[j] & 0xff) |
+ ((bytes[j + 1] & 0xff) << 8) |
+ ((bytes[j + 2] & 0xff) << 16) |
+ ((bytes[j + 3] & 0xff) << 24);
+ j += 4;
+ }
+
+ switch (bytes.Length - j)
+ {
+ case 3:
+ m_array[i] = ((bytes[j + 2] & 0xff) << 16);
+ goto case 2;
+ // fall through
+ case 2:
+ m_array[i] |= ((bytes[j + 1] & 0xff) << 8);
+ goto case 1;
+ // fall through
+ case 1:
+ m_array[i] |= (bytes[j] & 0xff);
+ break;
+ }
+
+ _version = 0;
+ }
+
+ public BitVec(bool[] values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException("values");
+ }
+
+ m_array = ArrayPool.Instance.GetArray(GetArrayLength(values.Length, BitsPerInt32));
+ m_length = values.Length;
+
+ for (int i = 0; i < values.Length; i++)
+ {
+ if (values[i])
+ m_array[i / 32] |= (1 << (i % 32));
+ }
+
+ _version = 0;
+
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold the bit values in values. values[0] represents
+ ** bits 0 - 31, values[1] represents bits 32 - 63, etc. The LSB of each
+ ** integer represents the lowest index value; values[0] & 1 represents bit
+ ** 0, values[0] & 2 represents bit 1, values[0] & 4 represents bit 2, etc.
+ **
+ ** Exceptions: ArgumentException if values == null.
+ =========================================================================*/
+ public BitVec(int[] values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException("values");
+ }
+
+ // this value is chosen to prevent overflow when computing m_length
+ if (values.Length > Int32.MaxValue / BitsPerInt32)
+ {
+ //throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", BitsPerInt32), "values");
+ }
+
+ m_array = ArrayPool.Instance.GetArray(values.Length);
+ m_length = values.Length * BitsPerInt32;
+
+ Array.Copy(values, m_array, values.Length);
+
+ _version = 0;
+ }
+
+ /*=========================================================================
+ ** Allocates a new BitVec with the same length and bit values as bits.
+ **
+ ** Exceptions: ArgumentException if bits == null.
+ =========================================================================*/
+ public BitVec(BitVec bits)
+ {
+ if (bits == null)
+ {
+ throw new ArgumentNullException("bits");
+ }
+
+ int arrayLength = GetArrayLength(bits.m_length, BitsPerInt32);
+ m_array = ArrayPool.Instance.GetArray(arrayLength);
+ m_length = bits.m_length;
+
+ Array.Copy(bits.m_array, m_array, arrayLength);
+
+ _version = bits._version;
+ }
+
+ public bool this[int index]
+ {
+ get
+ {
+ return Get(index);
+ }
+ set
+ {
+ Set(index, value);
+ }
+ }
+
+ /*=========================================================================
+ ** Returns the bit value at position index.
+ **
+ ** Exceptions: ArgumentOutOfRangeException if index < 0 or
+ ** index >= GetLength().
+ =========================================================================*/
+ public bool Get(int index)
+ {
+ if (index < 0 || index >= Length)
+ {
+ throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");
+ }
+
+ return (m_array[index / 32] & (1 << (index % 32))) != 0;
+ }
+
+ /*=========================================================================
+ ** Sets the bit value at position index to value.
+ **
+ ** Exceptions: ArgumentOutOfRangeException if index < 0 or
+ ** index >= GetLength().
+ =========================================================================*/
+ public void Set(int index, bool value)
+ {
+ if (index < 0 || index >= Length)
+ {
+ throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");
+ }
+
+ if (value)
+ {
+ m_array[index / 32] |= (1 << (index % 32));
+ }
+ else
+ {
+ m_array[index / 32] &= ~(1 << (index % 32));
+ }
+
+ _version++;
+ }
+
+ /*=========================================================================
+ ** Sets all the bit values to value.
+ =========================================================================*/
+ public void SetAll(bool value)
+ {
+ int fillValue = value ? unchecked(((int)0xffffffff)) : 0;
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++)
+ {
+ m_array[i] = fillValue;
+ }
+
+ _version++;
+ }
+
+ /*=========================================================================
+ ** Returns a reference to the current instance ANDed with value.
+ **
+ ** Exceptions: ArgumentException if value == null or
+ ** value.Length != this.Length.
+ =========================================================================*/
+ public BitVec And(BitVec value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ if (Length != value.Length)
+ throw new ArgumentException("Arg_ArrayLengthsDiffer");
+
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++)
+ {
+ m_array[i] &= value.m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ /*=========================================================================
+ ** Returns a reference to the current instance ORed with value.
+ **
+ ** Exceptions: ArgumentException if value == null or
+ ** value.Length != this.Length.
+ =========================================================================*/
+ public BitVec Or(BitVec value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ if (Length != value.Length)
+ throw new ArgumentException("Arg_ArrayLengthsDiffer");
+
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++)
+ {
+ m_array[i] |= value.m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ /*=========================================================================
+ ** Returns a reference to the current instance XORed with value.
+ **
+ ** Exceptions: ArgumentException if value == null or
+ ** value.Length != this.Length.
+ =========================================================================*/
+ public BitVec Xor(BitVec value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ if (Length != value.Length)
+ throw new ArgumentException("Arg_ArrayLengthsDiffer");
+
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++)
+ {
+ m_array[i] ^= value.m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ /*=========================================================================
+ ** Inverts all the bit values. On/true bit values are converted to
+ ** off/false. Off/false bit values are turned on/true. The current instance
+ ** is updated and returned.
+ =========================================================================*/
+ public BitVec Not()
+ {
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++)
+ {
+ m_array[i] = ~m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ public int Length
+ {
+ get
+ {
+ return m_length;
+ }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("value", "ArgumentOutOfRange_NeedNonNegNum");
+ }
+
+ int newints = GetArraySize(value, BitsPerInt32);
+ if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length)
+ {
+ // grow or shrink (if wasting more than _ShrinkThreshold ints)
+ int[] newarray = ArrayPool.Instance.GetArray(newints); //new int[newints];
+ Array.Copy(m_array, newarray, newints > m_array.Length ? m_array.Length : newints);
+ ArrayPool.Instance.PutArray(m_array);
+ m_array = newarray;
+ }
+
+ if (value > m_length)
+ {
+ // clear high bit values in the last int
+ int last = GetArrayLength(m_length, BitsPerInt32) - 1;
+ int bits = m_length % 32;
+ if (bits > 0)
+ {
+ m_array[last] &= (1 << bits) - 1;
+ }
+
+ // clear remaining int values
+ Array.Clear(m_array, last + 1, newints - last - 1);
+ }
+
+ m_length = value;
+ _version++;
+ }
+ }
+
+ // XPerY=n means that n Xs can be stored in 1 Y.
+ private const int BitsPerInt32 = 32;
+ private const int BytesPerInt32 = 4;
+ private const int BitsPerByte = 8;
+
+ ///
+ /// Used for conversion between different representations of bit array.
+ /// Returns (n+(div-1))/div, rearranged to avoid arithmetic overflow.
+ /// For example, in the bit to int case, the straightforward calc would
+ /// be (n+31)/32, but that would cause overflow. So instead it's
+ /// rearranged to ((n-1)/32) + 1, with special casing for 0.
+ ///
+ /// Usage:
+ /// GetArrayLength(77, BitsPerInt32): returns how many ints must be
+ /// allocated to store 77 bits.
+ ///
+ /// length of array
+ /// use a conversion constant, e.g. BytesPerInt32 to get
+ /// how many ints are required to store n bytes
+ /// length of the array
+ public static int GetArrayLength(int n, int div)
+ {
+ return n > 0 ? (((n - 1) / div) + 1) : 0;
+ }
+
+ private static int GetArraySize(int n, int div)
+ {
+ // compute the next highest power of 2 of 32-bit v
+ uint v = Convert.ToUInt32(GetArrayLength(n, div));
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+
+ return Convert.ToInt32(v);
+ }
+
+ public int[] m_array;
+ private int m_length;
+ private int _version;
+ private const int _ShrinkThreshold = 1024; //256;
+
+ private class ArrayPool
+ {
+ private Dictionary> dictionary;
+
+ private ArrayPool()
+ {
+ dictionary = new Dictionary>();
+ }
+
+ private static readonly ArrayPool instance = new ArrayPool();
+
+ public static ArrayPool Instance
+ {
+ get
+ {
+ return instance;
+ }
+ }
+
+ public int[] GetArray(int length)
+ {
+ ConcurrentBag arrays = GetBag(length);
+
+ int[] arr;
+ if (arrays.TryTake(out arr)) return arr;
+
+ return new int[length];
+ }
+
+ private ConcurrentBag GetBag(int length)
+ {
+ ConcurrentBag arrays;
+ if (!dictionary.ContainsKey(length))
+ {
+ arrays = new ConcurrentBag();
+ dictionary[length] = arrays;
+ }
+ else
+ {
+ arrays = dictionary[length];
+ }
+ return arrays;
+ }
+
+ public void PutArray(int[] arr)
+ {
+ ConcurrentBag arrays = GetBag(arr.Length);
+ Array.Clear(arr, 0, arr.Length);
+ arrays.Add(arr);
+ }
+ }
+ }
}
diff --git a/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs b/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs
index e368f43b4c..17d80f1214 100644
--- a/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs
+++ b/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs
@@ -49,7 +49,7 @@ private ColumnMapTranslator()
// replacement. Note that we will follow the chain of replacements, in
// case the replacement was also replaced.
//
- private static Var GetReplacementVar(Var originalVar, Dictionary replacementVarMap)
+ private static Var GetReplacementVar(Var originalVar, IDictionary replacementVarMap)
{
// SQLBUDT #478509: Follow the chain of mapped vars, don't
// just stop at the first one
@@ -124,7 +124,7 @@ internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary
// Replace VarRefColumnMaps with new VarRefColumnMaps with the specified Var
//
- internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary varToVarMap)
+ internal static ColumnMap Translate(ColumnMap columnMapToTranslate, IDictionary varToVarMap)
{
var result = Translate(
columnMapToTranslate,
diff --git a/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs b/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs
index fc7b9b92b4..541114bb5a 100644
--- a/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs
+++ b/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs
@@ -163,7 +163,7 @@ internal override bool IsPredicate(string name)
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpCopier")]
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
MessageId = "System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Assert(System.Boolean,System.String)")]
- private void MapCopiedNodeVars(IList sources, IList copies, Dictionary varMappings)
+ private void MapCopiedNodeVars(IList sources, IList copies, IDictionary varMappings)
{
PlanCompiler.Assert(sources.Count == copies.Count, "Source/Copy Node count mismatch");
diff --git a/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs b/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs
index 9a18404bdf..9c3c864080 100644
--- a/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs
+++ b/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs
@@ -2533,7 +2533,7 @@ private Node BuildUnionAllSubqueryForNestOp(
private static VarList GetUnionOutputs(UnionAllOp unionOp, VarList leftVars)
{
var varMap = unionOp.VarMap[0];
- Dictionary reverseVarMap = varMap.GetReverseMap();
+ IDictionary reverseVarMap = varMap.GetReverseMap();
var unionAllVars = Command.CreateVarList();
foreach (var v in leftVars)
diff --git a/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs b/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs
index 62d4c677e9..1da99a5fa2 100644
--- a/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs
+++ b/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs
@@ -57,7 +57,7 @@ internal class PlanCompiler
// Determines the maximum size of the query in terms of Iqt nodes for which we attempt to do transformation rules.
// This number is ignored if applyTransformationsRegardlessOfSize is enabled.
//
- private const int MaxNodeCountForTransformations = 100000;
+ private const int MaxNodeCountForTransformations = 10000;
//
// The CTree we're compiling a plan for.
diff --git a/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs b/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs
index 3c0f6fa56a..151939c78e 100644
--- a/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs
+++ b/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs
@@ -13,7 +13,7 @@ internal class VarRemapper : BasicOpVisitor
{
#region Private state
- private readonly Dictionary m_varMap;
+ private readonly IDictionary m_varMap;
protected readonly Command m_command;
#endregion
@@ -34,7 +34,7 @@ internal VarRemapper(Command command)
//
// Current iqt command
// Var map to be used
- internal VarRemapper(Command command, Dictionary varMap)
+ internal VarRemapper(Command command, IDictionary varMap)
{
m_command = command;
m_varMap = varMap;
@@ -102,7 +102,7 @@ internal VarList RemapVarList(VarList varList)
//
// Remap the given varList using the given varMap
//
- internal static VarList RemapVarList(Command command, Dictionary varMap, VarList varList)
+ internal static VarList RemapVarList(Command command, IDictionary varMap, VarList varList)
{
var varRemapper = new VarRemapper(command, varMap);
return varRemapper.RemapVarList(varList);
diff --git a/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs b/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs
index b27a7fd757..a80a5a1690 100644
--- a/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs
+++ b/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs
@@ -11,7 +11,8 @@ public class VarVecTests
[Fact]
public void MoveNext_returns_true_for_true_bits_and_false_when_end_is_reached()
{
- var enumerator = new VarVec.VarVecEnumerator(CreateVarVec(1));
+ var enumerator = new VarVec.VarVecEnumerator(CreateVarVec(1, 0, 1));
+ Assert.True(enumerator.MoveNext());
Assert.True(enumerator.MoveNext());
Assert.False(enumerator.MoveNext());
}