mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -05:00
Porting showplan code from ssms (#1280)
* Porting showplan code from ssms * Moving showplans bits to a subfolder * code cleanup * Remvoing unnecssary conditional visibility
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds hierarchy of Graph objects from SQL 2000 Actual ShowPlan Record Set
|
||||
/// </summary>
|
||||
internal class ActualPlanDataReaderNodeBuilder : DataReaderNodeBuilder
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Constructs ActualPlanDataReaderNodeBuilder
|
||||
/// </summary>
|
||||
public ActualPlanDataReaderNodeBuilder() : base()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
protected override ShowPlanType ShowPlanType
|
||||
{
|
||||
get { return ShowPlanType.Actual; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of Node Id in the recordset
|
||||
/// </summary>
|
||||
protected override int NodeIdIndex
|
||||
{
|
||||
get { return 4; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of Parent Id in the recordset
|
||||
/// </summary>
|
||||
protected override int ParentIndex
|
||||
{
|
||||
get { return 5; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets property names that correspond to values returned
|
||||
/// in each ShowPlan row.
|
||||
/// </summary>
|
||||
/// <returns>Array of property names</returns>
|
||||
protected override string[] GetPropertyNames()
|
||||
{
|
||||
return propertyNames;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private members
|
||||
|
||||
private static string[] propertyNames = new string[]
|
||||
{
|
||||
NodeBuilderConstants.ActualRows, // Rows
|
||||
NodeBuilderConstants.ActualExecutions, // Executes
|
||||
NodeBuilderConstants.StatementText, // StmtText
|
||||
null, // StmtId
|
||||
NodeBuilderConstants.NodeId,
|
||||
null, // Parent
|
||||
NodeBuilderConstants.PhysicalOp,
|
||||
NodeBuilderConstants.LogicalOp,
|
||||
NodeBuilderConstants.Argument,
|
||||
NodeBuilderConstants.DefinedValues,
|
||||
NodeBuilderConstants.EstimateRows,
|
||||
NodeBuilderConstants.EstimateIO,
|
||||
NodeBuilderConstants.EstimateCPU,
|
||||
NodeBuilderConstants.AvgRowSize,
|
||||
NodeBuilderConstants.TotalSubtreeCost,
|
||||
NodeBuilderConstants.OutputList,
|
||||
NodeBuilderConstants.Warnings,
|
||||
NodeBuilderConstants.StatementType, // Type
|
||||
NodeBuilderConstants.Parallel,
|
||||
null, // EstimateExecutions
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
#region FloatTypeConverter
|
||||
|
||||
/// <summary>
|
||||
/// FloatTypeConverter is used to get a desired float / double representation
|
||||
/// in the property sheet and the tool tip.
|
||||
/// The currently used scientific format isn't very readable
|
||||
///
|
||||
/// To use this converter, add the following attribute on top of the property:
|
||||
/// [TypeConverter(typeof(FloatTypeConverter))]
|
||||
/// </summary>
|
||||
|
||||
public class FloatTypeConverter : TypeConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the object value to another type.
|
||||
/// In this case the method only supports conversion to string.
|
||||
/// </summary>
|
||||
/// <param name="context">An ITypeDescriptorContext that provides a format context.</param>
|
||||
/// <param name="culture">A CultureInfo object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The Object to convert.</param>
|
||||
/// <param name="destinationType">The Type to convert the value parameter to.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(value);
|
||||
if (converter.CanConvertTo(typeof(double)))
|
||||
{
|
||||
double doubleValue = (double)converter.ConvertTo(value, typeof(double));
|
||||
return doubleValue.ToString("0.#######", CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DataSizeTypeConverter
|
||||
/// <summary>
|
||||
/// DataSizeTypeConverter is used to represent data size in bytes,
|
||||
/// kilobytes, megabytes, etc., depending on the actual number.
|
||||
///
|
||||
/// To use this converter, add the following attribute on top of the property:
|
||||
/// [TypeConverter(typeof(DataSizeTypeConverter))]
|
||||
/// </summary>
|
||||
|
||||
public class DataSizeTypeConverter : TypeConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the object value to another type.
|
||||
/// In this case the method only supports conversion to string.
|
||||
/// </summary>
|
||||
/// <param name="context">An ITypeDescriptorContext that provides a format context.</param>
|
||||
/// <param name="culture">A CultureInfo object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The Object to convert.</param>
|
||||
/// <param name="destinationType">The Type to convert the value parameter to.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
return this.ConvertTo(context, culture, value, destinationType, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the object value to another type.
|
||||
/// In this case the method only supports conversion to string.
|
||||
/// </summary>
|
||||
/// <param name="context">An ITypeDescriptorContext that provides a format context.</param>
|
||||
/// <param name="culture">A CultureInfo object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The Object to convert.</param>
|
||||
/// <param name="destinationType">The Type to convert the value parameter to.</param>
|
||||
/// <param name="formatIndex">The index in size formats to start with.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
protected object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType, int formatIndex)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(value);
|
||||
if (converter.CanConvertTo(typeof(double)))
|
||||
{
|
||||
double dataSize = (double)converter.ConvertTo(value, typeof(double));
|
||||
Debug.Assert(dataSize >= 0, "Data size must not be a negative number.");
|
||||
|
||||
// This cycle finds an optimal range for the size where no more than
|
||||
// 4 digits are displayed. Furthermore the result is rounded to a neareast
|
||||
// integer. So it will display sizes up to 9999 bytes in bytes then switch to
|
||||
// to 10K. Then it will go up to 9999 KB and switch to 10M.
|
||||
// Please note that 10000 bytes = 9.76 KB and will be rounded to 10 KB.
|
||||
while (formatIndex < sizeFormats.Length - 1)
|
||||
{
|
||||
if ((long)Math.Round(dataSize) < 10000)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dataSize /= 1024;
|
||||
formatIndex++;
|
||||
}
|
||||
|
||||
return String.Format(culture, sizeFormats[formatIndex], (long)Math.Round(dataSize));
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
static string[] sizeFormats = new string[]
|
||||
{
|
||||
SR.SizeInBytesFormat,
|
||||
SR.SizeInKiloBytesFormat,
|
||||
SR.SizeInMegaBytesFormat,
|
||||
SR.SizeInGigaBytesFormat,
|
||||
SR.SizeInTeraBytesFormat
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region KBSizeTypeConverter
|
||||
|
||||
/// <summary>
|
||||
/// KBSizeTypeConverter is used to represent data size in
|
||||
/// kilobytes, megabytes, etc., depending on the actual number.
|
||||
/// Assumes input is in kilobytes
|
||||
///
|
||||
/// To use this converter, add the following attribute on top of the property:
|
||||
/// [TypeConverter(typeof(KBSizeTypeConverter))]
|
||||
/// </summary>
|
||||
public sealed class KBSizeTypeConverter : DataSizeTypeConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the object value to another type.
|
||||
/// In this case the method only supports conversion to string.
|
||||
/// </summary>
|
||||
/// <param name="context">An ITypeDescriptorContext that provides a format context.</param>
|
||||
/// <param name="culture">A CultureInfo object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed.</param>
|
||||
/// <param name="value">The Object to convert.</param>
|
||||
/// <param name="destinationType">The Type to convert the value parameter to.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
// formatIndex of 1 indicates value of input should be in KB
|
||||
return base.ConvertTo(context, culture, value, destinationType, formatIndex: 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DisplayNameAndDescription
|
||||
|
||||
/// <summary>
|
||||
/// Describes property DisplayName and description keywords.
|
||||
/// </summary>
|
||||
public class DisplayNameDescriptionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Public default constructor
|
||||
/// </summary>
|
||||
/// <param name="displayName">Property DisplayName key.</param>
|
||||
public DisplayNameDescriptionAttribute(string displayName)
|
||||
{
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public default constructor
|
||||
/// </summary>
|
||||
/// <param name="displayName">Property DisplayName key.</param>
|
||||
/// <param name="description">Property Description key.</param>
|
||||
public DisplayNameDescriptionAttribute(string displayName, string description)
|
||||
{
|
||||
this.displayName = displayName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Display name
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
get { return this.displayName; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets Description
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return this.description; }
|
||||
}
|
||||
|
||||
private string displayName;
|
||||
private string description;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DisplayOrder
|
||||
|
||||
/// <summary>
|
||||
/// Represent order attribute. Tool tip window uses this attribute to sort properties accordingly
|
||||
/// </summary>
|
||||
public sealed class DisplayOrderAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Public default constructor
|
||||
/// </summary>
|
||||
/// <param name="displayOrder"></param>
|
||||
public DisplayOrderAttribute(int displayOrder)
|
||||
{
|
||||
this.displayOrder = displayOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display order
|
||||
/// </summary>
|
||||
public int DisplayOrder
|
||||
{
|
||||
get { return this.displayOrder; }
|
||||
}
|
||||
|
||||
private int displayOrder;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ShowInToolTip
|
||||
|
||||
/// <summary>
|
||||
/// Represent order attribute. Tool tip window uses this attribute to sort properties accordingly
|
||||
/// </summary>
|
||||
|
||||
public sealed class ShowInToolTipAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Public default constructor.
|
||||
/// </summary>
|
||||
public ShowInToolTipAttribute()
|
||||
{
|
||||
this.value = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor.
|
||||
/// </summary>
|
||||
/// <param name="value">Specifies whether the corresponding property should be visible in tool tips.
|
||||
/// The default value is true.</param>
|
||||
public ShowInToolTipAttribute(bool value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if a property should be shown in ToolTip; otherwise false.
|
||||
/// </summary>
|
||||
public bool Value
|
||||
{
|
||||
get { return this.value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if a property is a long string and should take an entire row in a tool tip; otherwise false.
|
||||
/// </summary>
|
||||
public bool LongString
|
||||
{
|
||||
get { return this.longString; }
|
||||
set { this.longString = value; }
|
||||
}
|
||||
|
||||
private bool value = true;
|
||||
private bool longString = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph.Comparison
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles operations for creating and comparing skeletons of showplan trees
|
||||
/// A skeleton is the tree with some nodes filtered out to reduce complexity
|
||||
/// </summary>
|
||||
public class SkeletonManager
|
||||
{
|
||||
public SkeletonManager() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a skeleton tree representing logical structure of the showplan
|
||||
/// Primarily represents joins and data access operators
|
||||
/// </summary>
|
||||
/// <param name="root">Node to construct skeleton of</param>
|
||||
/// <returns>SkeletonNode with children representing logical descendants of the input node</returns>
|
||||
public SkeletonNode CreateSkeleton(Node root)
|
||||
{
|
||||
Node rootNode = root;
|
||||
var childCount = root.Children.Count;
|
||||
if (childCount > 1)
|
||||
{
|
||||
SkeletonNode skeletonParent = new SkeletonNode(root);
|
||||
foreach (Node child in root.Children)
|
||||
{
|
||||
SkeletonNode skeletonChild = CreateSkeleton(child);
|
||||
skeletonParent.AddChild(skeletonChild);
|
||||
}
|
||||
return skeletonParent;
|
||||
}
|
||||
else if (childCount == 1)
|
||||
{
|
||||
if (!ShouldIgnoreDuringComparison(rootNode))
|
||||
{
|
||||
// get children recursively then return this node to add it to the skeleton
|
||||
SkeletonNode skeletonParent = new SkeletonNode(root);
|
||||
SkeletonNode child = CreateSkeleton(root.Children.ElementAt(0));
|
||||
skeletonParent.AddChild(child);
|
||||
return skeletonParent;
|
||||
}
|
||||
// if ignoring root, just go on to the next node
|
||||
return CreateSkeleton(root.Children.First());
|
||||
}
|
||||
// no children; base case
|
||||
SkeletonNode skeletonNode = new SkeletonNode(root);
|
||||
return skeletonNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks root and all children for equivalent tree structure and logical equivalence at the node level
|
||||
/// </summary>
|
||||
/// <param name="root1"></param>
|
||||
/// <param name="root2"></param>
|
||||
/// <returns></returns>
|
||||
public bool AreSkeletonsEquivalent(SkeletonNode root1, SkeletonNode root2, bool ignoreDatabaseName)
|
||||
{
|
||||
if (root1 == null && root2 == null)
|
||||
return true;
|
||||
|
||||
if (root1 == null || root2 == null)
|
||||
return false;
|
||||
|
||||
if (!root1.BaseNode.IsLogicallyEquivalentTo(root2.BaseNode, ignoreDatabaseName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (root1.Children.Count != root2.Children.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var childIterator = 0;
|
||||
while (childIterator < root1.Children.Count)
|
||||
{
|
||||
var checkMatch = AreSkeletonsEquivalent(root1.Children.ElementAt(childIterator), root2.Children.ElementAt(childIterator), ignoreDatabaseName);
|
||||
if (!checkMatch)
|
||||
{
|
||||
// at least one pair of children (ie inner.Child1 & outer.Child1) didn't match; stop checking rest
|
||||
return false;
|
||||
}
|
||||
childIterator++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the largest matching subtrees in two skeletons and colors those subtrees a unique color
|
||||
/// </summary>
|
||||
/// <param name="skeleton1"></param>
|
||||
/// <param name="skeleton2"></param>
|
||||
public void ColorMatchingSections(SkeletonNode skeleton1, SkeletonNode skeleton2, bool ignoreDatabaseName)
|
||||
{
|
||||
// starting node for the outer loop iteration
|
||||
SkeletonNode outerNode = skeleton1;
|
||||
Queue<SkeletonNode> outerQueue = new Queue<SkeletonNode>();
|
||||
|
||||
int groupIndexCounter = 1;
|
||||
|
||||
// Iterates over all nodes in skeleton1
|
||||
while (outerNode != null)
|
||||
{
|
||||
bool matchFound = false;
|
||||
SkeletonNode innerNode = skeleton2;
|
||||
Queue<SkeletonNode> innerQueue = new Queue<SkeletonNode>();
|
||||
// to find all the match sleleton2 node for skeleton1, iterate over each node of skeleton2 until all innerNode have been tested, there might be multiple match
|
||||
while (innerNode != null)
|
||||
{
|
||||
if (this.AreSkeletonsEquivalent(outerNode, innerNode, ignoreDatabaseName))
|
||||
{
|
||||
matchFound = true;
|
||||
int matchColor = groupIndexCounter++;
|
||||
if (innerNode.ParentNode != null && innerNode.ParentNode.HasMatch)
|
||||
{
|
||||
int parentColor = innerNode.ParentNode.GroupIndex;
|
||||
int innerColor = innerNode.GroupIndex;
|
||||
// innerNode is the root of a matching subtree, so use its color for the new match instead of the random new color
|
||||
if (parentColor != innerColor)
|
||||
{
|
||||
matchColor = innerColor;
|
||||
}
|
||||
}
|
||||
else if (innerNode.HasMatch)
|
||||
{
|
||||
matchColor = innerNode.GroupIndex;
|
||||
}
|
||||
else if (outerNode.HasMatch)
|
||||
{
|
||||
// outerNode already finds a matching innerNode, but we keep looking for more matching innerNode
|
||||
matchColor = outerNode.GroupIndex;
|
||||
}
|
||||
|
||||
outerNode.AddMatchingSkeletonNode(innerNode, ignoreDatabaseName);
|
||||
innerNode.AddMatchingSkeletonNode(outerNode, ignoreDatabaseName);
|
||||
outerNode.ChangeSkeletonGroupIndex(matchColor);
|
||||
innerNode.ChangeSkeletonGroupIndex(matchColor);
|
||||
}
|
||||
|
||||
// even if we found a matching innerNode, we keep looking since there might be other innerNode that matches same outerNode
|
||||
foreach (SkeletonNode child in innerNode.Children)
|
||||
{
|
||||
innerQueue.Enqueue(child);
|
||||
}
|
||||
innerNode = innerQueue.Any() ? innerQueue.Dequeue() : null;
|
||||
|
||||
}
|
||||
|
||||
// no match at all, so add this node's children to queue of nodes to check
|
||||
// effectively does a bfs - doesn't check children if a match has been found (and the entire subtree colored)
|
||||
if (!matchFound)
|
||||
{
|
||||
foreach (SkeletonNode child in outerNode.Children)
|
||||
{
|
||||
outerQueue.Enqueue(child);
|
||||
}
|
||||
}
|
||||
outerNode = outerQueue.Any() ? outerQueue.Dequeue() : null;
|
||||
}
|
||||
}
|
||||
|
||||
public Node FindNextNonIgnoreNode(Node node)
|
||||
{
|
||||
Node curNode = node;
|
||||
while (curNode != null && ShouldIgnoreDuringComparison(curNode))
|
||||
{
|
||||
if (curNode.Children.Count > 0)
|
||||
{
|
||||
curNode = curNode.Children.ElementAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// should ignore, but this is a leaf node, so there is no matching node
|
||||
curNode = null;
|
||||
}
|
||||
}
|
||||
return curNode;
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the node should be ignored when building a skeleton of the showplan
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
private bool ShouldIgnoreDuringComparison(Node node)
|
||||
{
|
||||
return IgnoreWhenBuildingSkeleton.Contains(node.Operation.Name) || (node[NodeBuilderConstants.LogicalOp] == null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// List of Node names which can safely be ignored when building the skeleton
|
||||
/// We can ignore these because not finding a matching between them shouldn't imapct of the shaping of matching nodes
|
||||
/// However, if in the future we see a use case to benefit from matching one of them, for ex I removed Filter because we need it to be skeleton node
|
||||
/// so we user can find issue for, and jump to the Filter node pair when doing scenario based issue detection
|
||||
/// </summary>
|
||||
private List<string> IgnoreWhenBuildingSkeleton = new List<string> { SR.Keys.Assert, SR.Keys.BatchHashTableBuild, SR.Keys.Bitmap, SR.Keys.Collapse, SR.Keys.RepartitionStreams,
|
||||
SR.Keys.ComputeScalar, SR.Keys.MergeInterval, SR.Keys.Parallelism, SR.Keys.Print, SR.Keys.RowCountSpool, SR.Keys.LogicalOpLazySpool,
|
||||
SR.Keys.TableSpool, SR.Keys.Segment, SR.Keys.SequenceProject, SR.Keys.Split, SR.Keys.Spool, SR.Keys.Window,
|
||||
SR.Keys.Sort, SR.Keys.Top, SR.Keys.LogicalOpTopNSort };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph.Comparison
|
||||
{
|
||||
public class SkeletonNode
|
||||
{
|
||||
public Node BaseNode {get; set;}
|
||||
public List<SkeletonNode> MatchingNodes { get; set; }
|
||||
public bool HasMatch { get { return MatchingNodes.Count > 0; } }
|
||||
public SkeletonNode ParentNode { get; set; }
|
||||
public IList<SkeletonNode> Children { get; set; }
|
||||
|
||||
public int GroupIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.BaseNode.GroupIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.BaseNode.GroupIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SkeletonNode(Node baseNode)
|
||||
{
|
||||
baseNode[NodeBuilderConstants.SkeletonNode] = this;
|
||||
this.BaseNode = baseNode;
|
||||
this.Children = new List<SkeletonNode>();
|
||||
this.MatchingNodes = new List<SkeletonNode>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds node to children collection and sets this node as parent of the child
|
||||
/// </summary>
|
||||
/// <param name="child"></param>
|
||||
public void AddChild(SkeletonNode child)
|
||||
{
|
||||
child.ParentNode = this;
|
||||
this.Children.Add(child);
|
||||
}
|
||||
|
||||
public void ChangeSkeletonGroupIndex(int groupIndex)
|
||||
{
|
||||
this.GroupIndex = groupIndex;
|
||||
foreach (SkeletonNode child in this.Children)
|
||||
{
|
||||
child.ChangeSkeletonGroupIndex(groupIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddMatchingSkeletonNode(SkeletonNode match, bool ignoreDatabaseName, bool matchAllChildren=true)
|
||||
{
|
||||
this.BaseNode[NodeBuilderConstants.SkeletonHasMatch] = true;
|
||||
if (matchAllChildren == true)
|
||||
{
|
||||
SkeletonManager manager = new SkeletonManager();
|
||||
foreach (SkeletonNode baseChild in this.Children)
|
||||
{
|
||||
foreach (SkeletonNode matchChild in match.Children)
|
||||
{
|
||||
// make sure this is the right child to match
|
||||
if (baseChild.BaseNode.IsLogicallyEquivalentTo(matchChild.BaseNode, ignoreDatabaseName))
|
||||
{
|
||||
baseChild.AddMatchingSkeletonNode(matchChild, ignoreDatabaseName, matchAllChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.MatchingNodes.Add(match);
|
||||
}
|
||||
|
||||
public Graph GetGraph()
|
||||
{
|
||||
return this.BaseNode.Graph;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
internal sealed class ConditionParser : XmlPlanHierarchyParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates FunctionType blocks and removes all items from UDF property.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public override IEnumerable<FunctionTypeItem> ExtractFunctions(object parsedItem)
|
||||
{
|
||||
StmtCondTypeCondition condition = parsedItem as StmtCondTypeCondition;
|
||||
if (condition != null && condition.UDF != null)
|
||||
{
|
||||
foreach (FunctionType function in condition.UDF)
|
||||
{
|
||||
yield return new FunctionTypeItem(function, FunctionTypeItem.ItemType.Udf);
|
||||
}
|
||||
|
||||
condition.UDF = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private ConditionParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static ConditionParser conditionParser = null;
|
||||
public static new ConditionParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (conditionParser == null)
|
||||
{
|
||||
conditionParser = new ConditionParser();
|
||||
}
|
||||
return conditionParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses StmtCursorType ShowPlan XML nodes
|
||||
/// </summary>
|
||||
internal class CursorOperationParser : XmlPlanParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates new node and adds it to the graph.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentItem">Parent item.</param>
|
||||
/// <param name="parentNode">Parent node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
/// <returns>The node that corresponds to the item being parsed.</returns>
|
||||
public override Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
return NewNode(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines Operation that corresponds to the object being parsed.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Operation that corresponds to the node.</returns>
|
||||
protected override Operation GetNodeOperation(Node node)
|
||||
{
|
||||
object cursorOperationName = node["OperationType"];
|
||||
|
||||
Operation cursorOperation = cursorOperationName != null
|
||||
? OperationTable.GetPhysicalOperation(cursorOperationName.ToString())
|
||||
: Operation.Unknown;
|
||||
|
||||
|
||||
return cursorOperation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines node subtree cost from existing node properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Node subtree cost.</returns>
|
||||
protected override double GetNodeSubtreeCost(Node node)
|
||||
{
|
||||
// This node doesn't have subtree cost, so it
|
||||
// will be determined based on child nodes.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private CursorOperationParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static CursorOperationParser cursorOperationParser = null;
|
||||
public static CursorOperationParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cursorOperationParser == null)
|
||||
{
|
||||
cursorOperationParser = new CursorOperationParser();
|
||||
}
|
||||
return cursorOperationParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses StmtCursorType ShowPlan XML nodes
|
||||
/// </summary>
|
||||
internal class CursorStatementParser : StatementParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines Operation that corresponds to the object being parsed.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Operation that corresponds to the node.</returns>
|
||||
protected override Operation GetNodeOperation(Node node)
|
||||
{
|
||||
object cursorType = node["CursorActualType"];
|
||||
|
||||
if (cursorType == null)
|
||||
{
|
||||
cursorType = node["StatementType"];
|
||||
}
|
||||
|
||||
Operation cursor = cursorType != null
|
||||
? OperationTable.GetCursorType(cursorType.ToString())
|
||||
: Operation.Unknown;
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private CursorStatementParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static CursorStatementParser cursorStatementParser = null;
|
||||
public static new CursorStatementParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cursorStatementParser == null)
|
||||
{
|
||||
cursorStatementParser = new CursorStatementParser();
|
||||
}
|
||||
return cursorStatementParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// 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;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for building hierarchy of Graph objects from ShowPlan Record Set
|
||||
/// </summary>
|
||||
internal abstract class DataReaderNodeBuilder: INodeBuilder
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes base class members.
|
||||
/// </summary>
|
||||
/// <param name="showPlanType">Show Plan type.</param>
|
||||
public DataReaderNodeBuilder()
|
||||
{}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INodeBuilder
|
||||
|
||||
/// <summary>
|
||||
/// Builds one or more Graphs that
|
||||
/// represnet data from the data source.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data Source.</param>
|
||||
/// <returns>An array of AnalysisServices Graph objects.</returns>
|
||||
public ShowPlanGraph[] Execute(object dataSource)
|
||||
{
|
||||
IDataReader reader = dataSource as IDataReader;
|
||||
|
||||
if (reader == null)
|
||||
{
|
||||
Debug.Assert(false, "Unexpected ShowPlan source = " + dataSource.GetType().ToString());
|
||||
throw new ArgumentException(SR.Keys.UnknownShowPlanSource);
|
||||
}
|
||||
|
||||
List<ShowPlanGraph> graphs = new List<ShowPlanGraph>();
|
||||
Dictionary<int, Node> currentGraphNodes = null;
|
||||
NodeBuilderContext context = null;
|
||||
|
||||
object[] values = null;
|
||||
string[] names = GetPropertyNames();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
ReadValues(reader, ref values);
|
||||
|
||||
int nodeID = (int)values[NodeIdIndex];
|
||||
int parentNodeID = (int)values[ParentIndex];
|
||||
|
||||
Node parentNode = null;
|
||||
|
||||
if (parentNodeID == 0)
|
||||
{
|
||||
// Starting a new graph
|
||||
// First add an old graph to the list
|
||||
if (context != null)
|
||||
{
|
||||
graphs.Add(context.Graph);
|
||||
}
|
||||
|
||||
// Create new Context and new Nodes hashtable
|
||||
context = new NodeBuilderContext(new ShowPlanGraph(), ShowPlanType, this);
|
||||
currentGraphNodes = new Dictionary<int, Node>();
|
||||
}
|
||||
else
|
||||
{
|
||||
parentNode = currentGraphNodes[parentNodeID];
|
||||
}
|
||||
|
||||
// Create new node.
|
||||
Debug.Assert(context != null);
|
||||
Node node = CreateNode(nodeID, context);
|
||||
|
||||
ParseProperties(node, names, values);
|
||||
SetNodeSpecialProperties(node);
|
||||
|
||||
if (parentNode != null)
|
||||
{
|
||||
parentNode.Children.AddLast(node);
|
||||
}
|
||||
|
||||
// Add node to the hashtable
|
||||
// In some rare cases the graph may already
|
||||
// contain the node with the same ID.
|
||||
// This happens, for example, in a case of
|
||||
// Table Spool node. In this case it is safe
|
||||
// to not add the node to the currentGraphNodes collection
|
||||
// because it isn't going to have any children (guaranteed)
|
||||
if (!currentGraphNodes.ContainsKey(nodeID))
|
||||
{
|
||||
currentGraphNodes.Add(nodeID, node);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last parsed graph to the list of graphs.
|
||||
if (context != null)
|
||||
{
|
||||
graphs.Add(context.Graph);
|
||||
}
|
||||
|
||||
return graphs.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets property names that correspond to values returned
|
||||
/// in each ShowPlan row.
|
||||
/// </summary>
|
||||
/// <returns>Array of property names</returns>
|
||||
protected abstract string[] GetPropertyNames();
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of Node Id in the recordset
|
||||
/// </summary>
|
||||
protected abstract int NodeIdIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of Parent Id in the recordset
|
||||
/// </summary>
|
||||
protected abstract int ParentIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ShowPlanType of hte resordset
|
||||
/// </summary>
|
||||
protected abstract ShowPlanType ShowPlanType{ get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sequentially reads all columns from IDataReader
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="values"></param>
|
||||
private void ReadValues(IDataReader reader, ref object[] values)
|
||||
{
|
||||
if (values == null || reader.FieldCount != values.Length)
|
||||
{
|
||||
values = new object[reader.FieldCount];
|
||||
}
|
||||
|
||||
// We specifically need to read values sequentially
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = reader.GetValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads properties from the data row.
|
||||
/// </summary>
|
||||
/// <param name="node">Node which is populated with properties</param>
|
||||
/// <param name="names">Property names.</param>
|
||||
/// <param name="values">Property values.</param>
|
||||
private void ParseProperties(Node node, string[] names, object[] values)
|
||||
{
|
||||
int count = Math.Min(names.Length, values.Length);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (names[i] != null && !((values[i] is DBNull) || values[i] == null))
|
||||
{
|
||||
node[names[i]] = values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets special properties on the node.
|
||||
/// </summary>
|
||||
/// <param name="node">Node.</param>
|
||||
private static void SetNodeSpecialProperties(Node node)
|
||||
{
|
||||
// SubtreeCost is a special property that should be set separately
|
||||
node.SubtreeCost = GetNodeSubtreeCost(node);
|
||||
|
||||
Operation resultOp;
|
||||
|
||||
string nodeType = (string)node["StatementType"];
|
||||
if (string.Compare(nodeType, "PLAN_ROW", StringComparison.OrdinalIgnoreCase) != 0)
|
||||
{
|
||||
// This is a statement
|
||||
resultOp = OperationTable.GetStatement(nodeType);
|
||||
|
||||
node["LogicalOp"] = resultOp.DisplayName;
|
||||
node["PhysicalOp"] = resultOp.DisplayName;
|
||||
|
||||
// For statements, Argument is the same as the Statement text (if any defined)
|
||||
node["Argument"] = node["StatementText"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an operation node
|
||||
|
||||
// Remove StatementText property
|
||||
PropertyDescriptor statementTextProperty = node.Properties["StatementText"];
|
||||
if (statementTextProperty != null)
|
||||
{
|
||||
node.Properties.Remove(statementTextProperty);
|
||||
}
|
||||
|
||||
// Special consideration for Argument property:
|
||||
// Try to parse Object name from it
|
||||
string argument = node["Argument"] as string;
|
||||
if (argument != null)
|
||||
{
|
||||
Match match = argumentObjectExpression.Match(argument);
|
||||
if (match != Match.Empty)
|
||||
{
|
||||
node["Object"] = match.Groups["Object"].Value;
|
||||
}
|
||||
}
|
||||
|
||||
string physicalOpType = node["PhysicalOp"] as string;
|
||||
string logicalOpType = node["LogicalOp"] as string;
|
||||
|
||||
if (physicalOpType == null || logicalOpType == null)
|
||||
{
|
||||
throw new FormatException(SR.Keys.UnknownShowPlanSource);
|
||||
}
|
||||
|
||||
// Remove spaces and other special characters from physical and logical names
|
||||
physicalOpType = operatorReplaceExpression.Replace(physicalOpType, "");
|
||||
logicalOpType = operatorReplaceExpression.Replace(logicalOpType, "");
|
||||
|
||||
Operation physicalOp = OperationTable.GetPhysicalOperation(physicalOpType);
|
||||
Operation logicalOp = OperationTable.GetLogicalOperation(logicalOpType);
|
||||
|
||||
resultOp = logicalOp != null && logicalOp.Image != null && logicalOp.Description != null
|
||||
? logicalOp : physicalOp;
|
||||
|
||||
node["LogicalOp"] = logicalOp.DisplayName;
|
||||
node["PhysicalOp"] = physicalOp.DisplayName;
|
||||
|
||||
// EstimateExecutions = EstimateRebinds + EstimateRewinds + 1
|
||||
if (node["EstimateRebinds"] != null && node["EstimateRewinds"] != null)
|
||||
{
|
||||
double estimateRebinds = (double) node["EstimateRebinds"];
|
||||
double estimateRewinds = (double) node["EstimateRewinds"];
|
||||
node["EstimateExecutions"] = estimateRebinds + estimateRewinds + 1;
|
||||
}
|
||||
|
||||
// EstimateRowsAllExecs = EstimateRows * EstimateExecutions
|
||||
double estimateRows = node["EstimateRows"] == null ? 0.0 : Convert.ToDouble(node["EstimateRows"]);
|
||||
double estimateExecutions = node["EstimateExecutions"] == null ? 0.0 : Convert.ToDouble(node["EstimateExecutions"]);
|
||||
double actualExecutions = node["ActualExecutions"] == null ? 0.0 : Convert.ToDouble(node["ActualExecutions"]);
|
||||
node["EstimateRowsAllExecs"] = estimateRows * estimateExecutions;
|
||||
}
|
||||
|
||||
Debug.Assert(resultOp.Image != null);
|
||||
node.Operation = resultOp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 'Factory' method for creating Nodes, allows for subclasses to override
|
||||
/// </summary>
|
||||
protected virtual Node CreateNode(int nodeId, NodeBuilderContext context)
|
||||
{
|
||||
return new Node(nodeId, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines node subtree cost from existing node properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Node subtree cost.</returns>
|
||||
private static double GetNodeSubtreeCost(Node node)
|
||||
{
|
||||
object value = node["TotalSubtreeCost"];
|
||||
return value != null ? Convert.ToDouble(value, CultureInfo.CurrentCulture) : 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private members
|
||||
|
||||
private static Regex operatorReplaceExpression = new Regex(@"[ \-]", RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
private static Regex argumentObjectExpression = new Regex(@"OBJECT:\((?<Object>[^\)]*)\)", RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public class Description
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return this.title; }
|
||||
set
|
||||
{
|
||||
this.title = value.Trim().Replace(NewLine, " ");
|
||||
}
|
||||
}
|
||||
|
||||
public string QueryText
|
||||
{
|
||||
get { return this.queryText; }
|
||||
set
|
||||
{
|
||||
string text = value.Trim();
|
||||
this.queryText = text.Replace(NewLine, " ");
|
||||
}
|
||||
}
|
||||
|
||||
public string ClusteredMode
|
||||
{
|
||||
get { return this.clusteredMode; }
|
||||
set
|
||||
{
|
||||
this.clusteredMode = value.Trim().Replace(NewLine, " ");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsClusteredMode
|
||||
{
|
||||
set
|
||||
{
|
||||
this.isClusteredMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasMissingIndex
|
||||
{
|
||||
get { return this.hasMissingIndex; }
|
||||
}
|
||||
|
||||
public string MissingIndexQueryText
|
||||
{
|
||||
get { return this.missingIndexQueryText; }
|
||||
}
|
||||
|
||||
public string MissingIndexImpact
|
||||
{
|
||||
get { return this.missingIndexImpact; }
|
||||
}
|
||||
|
||||
public string MissingIndexDatabase
|
||||
{
|
||||
get { return this.missingIndexDatabase; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Member variables
|
||||
|
||||
private string title = string.Empty;
|
||||
private string queryText = string.Empty;
|
||||
private string toolTipQueryText = string.Empty;
|
||||
private string clusteredMode = string.Empty;
|
||||
private bool isClusteredMode = false;
|
||||
|
||||
private bool hasMissingIndex = false;
|
||||
private string missingIndexCaption = string.Empty; // actual caption text that will be displayed on the screen
|
||||
private string missingIndexQueryText = string.Empty; // create index query
|
||||
private string missingIndexImpact = string.Empty; // impact
|
||||
private string missingIndexDatabase = string.Empty; // database context
|
||||
|
||||
private const string NewLine = "\r\n";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public class Edge
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
public Node FromNode;
|
||||
public Node ToNode;
|
||||
|
||||
public Edge(Node fromNode, Node toNode)
|
||||
{
|
||||
Initialize(toNode as Node);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties and methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets Edge properties.
|
||||
/// </summary>
|
||||
public PropertyDescriptorCollection Properties
|
||||
{
|
||||
get { return this.properties; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets node property value.
|
||||
/// </summary>
|
||||
public object this[string propertyName]
|
||||
{
|
||||
get
|
||||
{
|
||||
PropertyValue property = this.properties[propertyName] as PropertyValue;
|
||||
return property != null ? property.Value : null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
PropertyValue property = this.properties[propertyName] as PropertyValue;
|
||||
if (property != null)
|
||||
{
|
||||
// Overwrite existing property value
|
||||
property.Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new property
|
||||
this.properties.Add(PropertyFactory.CreateProperty(propertyName, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double RowSize
|
||||
{
|
||||
get
|
||||
{
|
||||
object propertyValue = this["AvgRowSize"];
|
||||
return propertyValue != null ? Convert.ToDouble(propertyValue, CultureInfo.CurrentCulture) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public double RowCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if(this["ActualRowsRead"] == null && this["ActualRows"] == null)
|
||||
{
|
||||
// If Actual Row count and ActualRowsRead are not set, default to estimated row count
|
||||
return EstimatedRowCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// at least one of ActualRowsRead and ActualRows is set
|
||||
double actualRowsReadValue = 0;
|
||||
double actualRowsValue = 0;
|
||||
if (this["ActualRowsRead"] != null)
|
||||
{
|
||||
actualRowsReadValue = Convert.ToDouble(this["ActualRowsRead"].ToString(), CultureInfo.CurrentCulture);
|
||||
}
|
||||
if (this["ActualRows"] != null)
|
||||
{
|
||||
actualRowsValue = Convert.ToDouble(this["ActualRows"].ToString(), CultureInfo.CurrentCulture);
|
||||
}
|
||||
return actualRowsReadValue > actualRowsValue ? actualRowsReadValue : actualRowsValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double EstimatedRowCount
|
||||
{
|
||||
get
|
||||
{
|
||||
object propertyValue = this["EstimateRows"];
|
||||
if (propertyValue == null)
|
||||
{
|
||||
propertyValue = this["StatementEstRows"];
|
||||
}
|
||||
|
||||
return propertyValue != null ? Convert.ToDouble(propertyValue, CultureInfo.CurrentCulture) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public double EstimatedDataSize
|
||||
{
|
||||
get
|
||||
{
|
||||
object propertyValue = this["EstimatedDataSize"];
|
||||
return propertyValue != null ? Convert.ToDouble(propertyValue, CultureInfo.CurrentCulture) : 0;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
this["EstimatedDataSize"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation details
|
||||
|
||||
/// <summary>
|
||||
/// Copy some of edge properties from the node connected through this edge.
|
||||
/// </summary>
|
||||
/// <param name="node">The node connected on the right side of the edge.</param>
|
||||
private void Initialize(Node node)
|
||||
{
|
||||
this.properties = new PropertyDescriptorCollection(new PropertyDescriptor[] {});
|
||||
|
||||
string[] propertyNames = new string[]
|
||||
{
|
||||
"ActualRows",
|
||||
"ActualRowsRead",
|
||||
"AvgRowSize",
|
||||
"EstimateRows",
|
||||
"EstimateRowsAllExecs",
|
||||
"StatementEstRows"
|
||||
};
|
||||
|
||||
// Copy properties
|
||||
foreach (string propertyName in propertyNames)
|
||||
{
|
||||
object value = node[propertyName];
|
||||
if (value != null)
|
||||
{
|
||||
this[propertyName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this.EstimatedDataSize = this.RowSize * this.EstimatedRowCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
private PropertyDescriptorCollection properties;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds hierarchy of Graph objects from SQL 2000 Estimated ShowPlan Record Set
|
||||
/// </summary>
|
||||
internal sealed class EstimatedPlanDataReaderNodeBuilder : DataReaderNodeBuilder
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Constructs EstimatedPlanDataReaderNodeBuilder
|
||||
/// </summary>
|
||||
public EstimatedPlanDataReaderNodeBuilder() : base()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
protected override ShowPlanType ShowPlanType
|
||||
{
|
||||
get { return ShowPlanType.Estimated; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of Node Id in the recordset
|
||||
/// </summary>
|
||||
protected override int NodeIdIndex
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of Parent Id in the recordset
|
||||
/// </summary>
|
||||
protected override int ParentIndex
|
||||
{
|
||||
get { return 3; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets property names that correspond to values returned
|
||||
/// in each ShowPlan row.
|
||||
/// </summary>
|
||||
/// <returns>Array of property names</returns>
|
||||
protected override string[] GetPropertyNames()
|
||||
{
|
||||
return propertyNames;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private members
|
||||
|
||||
private static string[] propertyNames = new string[]
|
||||
{
|
||||
NodeBuilderConstants.StatementText, // StmtText
|
||||
null, // StmtId
|
||||
NodeBuilderConstants.NodeId,
|
||||
null, // Parent
|
||||
NodeBuilderConstants.PhysicalOp,
|
||||
NodeBuilderConstants.LogicalOp,
|
||||
NodeBuilderConstants.Argument,
|
||||
NodeBuilderConstants.DefinedValues,
|
||||
NodeBuilderConstants.EstimateRows,
|
||||
NodeBuilderConstants.EstimateIO,
|
||||
NodeBuilderConstants.EstimateCPU,
|
||||
NodeBuilderConstants.AvgRowSize,
|
||||
NodeBuilderConstants.TotalSubtreeCost,
|
||||
NodeBuilderConstants.OutputList,
|
||||
NodeBuilderConstants.Warnings,
|
||||
NodeBuilderConstants.StatementType, // Type
|
||||
NodeBuilderConstants.Parallel,
|
||||
NodeBuilderConstants.EstimateExecutions
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
|
||||
public class ExpandableArrayWrapper : ExpandableObjectWrapper
|
||||
{
|
||||
public ExpandableArrayWrapper(ICollection collection) : base()
|
||||
{
|
||||
PopulateProperties(collection);
|
||||
}
|
||||
|
||||
#region Implementation details
|
||||
|
||||
private void PopulateProperties(ICollection collection)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int index = 0;
|
||||
|
||||
foreach (object item in collection)
|
||||
{
|
||||
if (ObjectWrapperTypeConverter.Default.CanConvertFrom(item.GetType()))
|
||||
{
|
||||
object convertedItem = ObjectWrapperTypeConverter.Default.ConvertFrom(item);
|
||||
if (convertedItem != null)
|
||||
{
|
||||
this[GetPropertyName(++index)] = convertedItem;
|
||||
|
||||
if (stringBuilder.Length > 0)
|
||||
{
|
||||
stringBuilder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator);
|
||||
stringBuilder.Append(" ");
|
||||
}
|
||||
|
||||
stringBuilder.Append(convertedItem.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.DisplayName = stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string GetPropertyName(int index)
|
||||
{
|
||||
return String.Format(CultureInfo.CurrentCulture, "[{0}]", index);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public class ExpandableObjectWrapper : ObjectParser, ICustomTypeDescriptor
|
||||
{
|
||||
public ExpandableObjectWrapper()
|
||||
: this(null, null, String.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public ExpandableObjectWrapper(object item)
|
||||
: this(item, null)
|
||||
{
|
||||
}
|
||||
|
||||
public ExpandableObjectWrapper(object item, string defaultPropertyName)
|
||||
: this(item, defaultPropertyName, GetDefaultDisplayName(item))
|
||||
{
|
||||
}
|
||||
|
||||
public ExpandableObjectWrapper(object item, string defaultPropertyName, string displayName)
|
||||
{
|
||||
this.properties = new PropertyDescriptorCollection(new PropertyDescriptor[]{});
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
ParseProperties(item, this.properties, null);
|
||||
}
|
||||
|
||||
if (defaultPropertyName != null)
|
||||
{
|
||||
defaultProperty = this.properties[defaultPropertyName];
|
||||
}
|
||||
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets node property value.
|
||||
/// </summary>
|
||||
public object this[string propertyName]
|
||||
{
|
||||
get
|
||||
{
|
||||
PropertyValue property = this.properties[propertyName] as PropertyValue;
|
||||
return property != null ? property.Value : null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
PropertyValue property = this.properties[propertyName] as PropertyValue;
|
||||
if (property != null)
|
||||
{
|
||||
// Overwrite existing property value
|
||||
property.Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new property
|
||||
this.properties.Add(new PropertyValue(propertyName, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public string DisplayName
|
||||
{
|
||||
get { return this.displayName; }
|
||||
set { this.displayName = value; }
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public PropertyDescriptorCollection Properties
|
||||
{
|
||||
get { return this.properties; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result of item.ToString if it isn't the item class name.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to stringize.</param>
|
||||
/// <returns>Default item display name.</returns>
|
||||
public static string GetDefaultDisplayName(object item)
|
||||
{
|
||||
string itemString = item.ToString();
|
||||
return itemString != item.GetType().ToString() ? itemString : String.Empty;
|
||||
}
|
||||
|
||||
#region ICustomTypeDescriptor
|
||||
|
||||
AttributeCollection ICustomTypeDescriptor.GetAttributes()
|
||||
{
|
||||
return TypeDescriptor.GetAttributes(GetType());
|
||||
}
|
||||
|
||||
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
|
||||
{
|
||||
return TypeDescriptor.GetDefaultEvent(GetType());
|
||||
}
|
||||
|
||||
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
|
||||
{
|
||||
return defaultProperty;
|
||||
}
|
||||
|
||||
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
|
||||
{
|
||||
return TypeDescriptor.GetEditor(GetType(), editorBaseType);
|
||||
}
|
||||
|
||||
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
|
||||
{
|
||||
return TypeDescriptor.GetEvents(GetType());
|
||||
}
|
||||
|
||||
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
|
||||
{
|
||||
return TypeDescriptor.GetEvents(GetType(), attributes);
|
||||
}
|
||||
|
||||
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
|
||||
{
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
|
||||
{
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
string ICustomTypeDescriptor.GetComponentName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeConverter ICustomTypeDescriptor.GetConverter()
|
||||
{
|
||||
return TypeDescriptor.GetConverter(GetType());
|
||||
}
|
||||
|
||||
string ICustomTypeDescriptor.GetClassName()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private PropertyDescriptorCollection properties;
|
||||
private PropertyDescriptor defaultProperty;
|
||||
private string displayName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses ShowPlan XML objects derived from RelOpBaseType type
|
||||
/// </summary>
|
||||
internal sealed class FilterTypeParser : RelOpBaseTypeParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private FilterTypeParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates node special properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
public override void ParseProperties(object parsedItem, PropertyDescriptorCollection targetPropertyBag, NodeBuilderContext context)
|
||||
{
|
||||
base.ParseProperties(parsedItem, targetPropertyBag, context);
|
||||
|
||||
FilterType item = parsedItem as FilterType;
|
||||
Debug.Assert(item != null, "FilterType object expected");
|
||||
|
||||
if (item.StartupExpression)
|
||||
{
|
||||
// If the filter has Predicate property, it has to be renamed to
|
||||
// Startup Expression Predicate
|
||||
PropertyValue property = targetPropertyBag["Predicate"] as PropertyValue;
|
||||
if (property != null)
|
||||
{
|
||||
property.SetDisplayNameAndDescription(SR.StartupExpressionPredicate, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static FilterTypeParser filterTypeParser = null;
|
||||
public static new FilterTypeParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (filterTypeParser == null)
|
||||
{
|
||||
filterTypeParser = new FilterTypeParser();
|
||||
}
|
||||
return filterTypeParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
internal sealed class FunctionTypeParser : XmlPlanParser
|
||||
{
|
||||
/// <summary>
|
||||
/// This function doesn't do anything. It simply returns the parent node
|
||||
/// passed it.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentItem">Parent item.</param>
|
||||
/// <param name="parentNode">Parent node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
/// <returns>The node that corresponds to the item being parsed.</returns>
|
||||
public override Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
Node currentNode = NewNode(context);
|
||||
|
||||
bool isStoredProcedure = false;
|
||||
|
||||
if (parentItem != null)
|
||||
{
|
||||
PropertyDescriptor storedProcProperty = TypeDescriptor.GetProperties(parentItem)["StoredProc"];
|
||||
|
||||
// If parent item has "StoredProc" property and it references the current item
|
||||
// then this item is a Stored Procedure. Otherwise it is an UDF.
|
||||
if (storedProcProperty != null && storedProcProperty.GetValue(parentItem) == item)
|
||||
{
|
||||
isStoredProcedure = true;
|
||||
}
|
||||
}
|
||||
|
||||
currentNode.Operation = isStoredProcedure ? OperationTable.GetStoredProc() : OperationTable.GetUdf();
|
||||
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines Operation that corresponds to the object being parsed.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Operation that corresponds to the node.</returns>
|
||||
protected override Operation GetNodeOperation(Node node)
|
||||
{
|
||||
|
||||
// Node operation is defined above based on parent item.
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines node subtree cost from existing node properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Node subtree cost.</returns>
|
||||
protected override double GetNodeSubtreeCost(Node node)
|
||||
{
|
||||
// This node doesn't have subtree cost, so it
|
||||
// will be determined based on child nodes.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private FunctionTypeParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static FunctionTypeParser functionTypeParser = null;
|
||||
public static FunctionTypeParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (functionTypeParser == null)
|
||||
{
|
||||
functionTypeParser = new FunctionTypeParser();
|
||||
}
|
||||
return functionTypeParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public class Graph
|
||||
{
|
||||
public Node Root;
|
||||
|
||||
public Description Description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface represents an abstract builder that gets
|
||||
/// data from the data source and represents it as
|
||||
/// an array of AnalysisServices Graph objects.
|
||||
/// </summary>
|
||||
public interface INodeBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds one or more Graphs that
|
||||
/// represnet data from the data source.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data Source.</param>
|
||||
/// <returns>An array of AnalysisServices Graph objects.</returns>
|
||||
ShowPlanGraph[] Execute(object dataSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface represents ability to split an data source containing multiple
|
||||
/// batches / statement into statements and return an XML containing a single statement.
|
||||
/// This is used for XML ShowPlan saving.
|
||||
/// </summary>
|
||||
public interface IXmlBatchParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds one or more Graphs that
|
||||
/// represnet data from the data source.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data Source.</param>
|
||||
/// <returns>An array of AnalysisServices Graph objects.</returns>
|
||||
string GetSingleStatementXml(object dataSource, int statementIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Returns statements block type object
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data source</param>
|
||||
/// <param name="statementIndex">Statement index in the data source</param>
|
||||
/// <returns>Statement block type object</returns>
|
||||
StmtBlockType GetSingleStatementObject(object dataSource, int statementIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses ShowPlan XML objects derived from RelOpBaseType type
|
||||
/// </summary>
|
||||
internal sealed class IndexOpTypeParser : RelOpBaseTypeParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private IndexOpTypeParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static IndexOpTypeParser indexOpTypeParser = null;
|
||||
public static new IndexOpTypeParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (indexOpTypeParser == null)
|
||||
{
|
||||
indexOpTypeParser = new IndexOpTypeParser();
|
||||
}
|
||||
return indexOpTypeParser;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the ObjectType that the Index operation references.
|
||||
/// </summary>
|
||||
/// <param name="indexScanType">The current Index operation node being parsed</param>
|
||||
private ObjectType GetObjectTypeFromProperties(object parsedItem)
|
||||
{
|
||||
ObjectType objectType = null;
|
||||
|
||||
// The index operators operate on an object, get that object.
|
||||
PropertyDescriptor objectProperty = TypeDescriptor.GetProperties(parsedItem)["Object"];
|
||||
Debug.Assert(objectProperty != null, "Object expected");
|
||||
if (objectProperty != null)
|
||||
{
|
||||
object objectItem = objectProperty.GetValue(parsedItem);
|
||||
if (objectItem != null)
|
||||
{
|
||||
ObjectType[] objectTypeArray = objectItem as ObjectType[];
|
||||
Debug.Assert(objectTypeArray != null && objectTypeArray.Length == 1, "ObjectTypeArray is null or more than one object found for IndexScan");
|
||||
|
||||
// Only handle the index operations operate on one object
|
||||
if (objectTypeArray != null && objectTypeArray.Length == 1)
|
||||
{
|
||||
objectType = objectTypeArray[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objectType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the indexKind attribute, if it exists, from the ObjectType as a property in the targetPropertyBag.
|
||||
/// </summary>
|
||||
/// <param name="objectType">The objectType for the indexScan.</param>
|
||||
/// <param name="targetPropertyBag">The target the property bag where we will put the PhysicalOperationKind element.</param>
|
||||
private void AddIndexKindAsPhysicalOperatorKind(ObjectType objectType, PropertyDescriptorCollection targetPropertyBag)
|
||||
{
|
||||
if (objectType.IndexKindSpecified)
|
||||
{
|
||||
if (0 < objectType.IndexKind.ToString().Length)
|
||||
{
|
||||
PropertyDescriptor wrapperProperty = PropertyFactory.CreateProperty("PhysicalOperationKind", objectType.IndexKind.ToString());
|
||||
if (wrapperProperty != null)
|
||||
{
|
||||
targetPropertyBag.Add(wrapperProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates node special properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed. The node should be IndexScanType or CreateIndexType</param>
|
||||
public override void ParseProperties(object parsedItem, PropertyDescriptorCollection targetPropertyBag, NodeBuilderContext context)
|
||||
{
|
||||
Debug.Assert((parsedItem is IndexScanType) || (parsedItem is CreateIndexType), "IndexScanType or CreateIndexType object expected");
|
||||
|
||||
// Parse the item as usual with RelOpBaseTypeParser first
|
||||
base.ParseProperties(parsedItem, targetPropertyBag, context);
|
||||
|
||||
// Now look for the object and get the indexKind
|
||||
if ((parsedItem is IndexScanType) || (parsedItem is CreateIndexType))
|
||||
{
|
||||
ObjectType objectType = this.GetObjectTypeFromProperties(parsedItem);
|
||||
if (objectType != null)
|
||||
{
|
||||
this.AddIndexKindAsPhysicalOperatorKind(objectType, targetPropertyBag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses ShowPlan XML objects derived from RelOpBaseType type
|
||||
/// </summary>
|
||||
internal sealed class MergeTypeParser : RelOpBaseTypeParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private MergeTypeParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates node special properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
public override void ParseProperties(object parsedItem, PropertyDescriptorCollection targetPropertyBag, NodeBuilderContext context)
|
||||
{
|
||||
base.ParseProperties(parsedItem, targetPropertyBag, context);
|
||||
|
||||
MergeType item = parsedItem as MergeType;
|
||||
Debug.Assert(item != null, "MergeType object expected");
|
||||
|
||||
// Make a new property which combines "InnerSideJoinColumns" and "OuterSideJoinColumns"
|
||||
object mergeColumnsWrapper = ObjectWrapperTypeConverter.Convert(new MergeColumns(item));
|
||||
PropertyDescriptor wrapperProperty = PropertyFactory.CreateProperty("WhereJoinColumns", mergeColumnsWrapper);
|
||||
if (wrapperProperty != null)
|
||||
{
|
||||
targetPropertyBag.Add(wrapperProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a property should be skipped from the target property bag
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
protected override bool ShouldSkipProperty(PropertyDescriptor property)
|
||||
{
|
||||
if (property.Name == "InnerSideJoinColumns" || property.Name == "OuterSideJoinColumns")
|
||||
{
|
||||
// These two properties are handled in a special way
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.ShouldSkipProperty(property);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static MergeTypeParser mergeTypeParser = null;
|
||||
public static new MergeTypeParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mergeTypeParser == null)
|
||||
{
|
||||
mergeTypeParser = new MergeTypeParser();
|
||||
}
|
||||
return mergeTypeParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This type is used for 2 purposes:
|
||||
/// 1) It creates additional level in the property hierarchy. Instead of including
|
||||
/// InnerSideJoinColumns and OuterSideJoinColumnsField properties in the Node, we
|
||||
/// create additional property which has these two properties as nested properties.
|
||||
/// 2) It allows to convert this to string the same way we convert other custom types
|
||||
/// See static Convert(MergeColumns) method in ObjectWrapperTypeConverter.cs
|
||||
/// </summary>
|
||||
public sealed class MergeColumns
|
||||
{
|
||||
public MergeColumns(MergeType mergeType)
|
||||
{
|
||||
this.innerSideJoinColumnsField = mergeType.InnerSideJoinColumns;
|
||||
this.outerSideJoinColumnsField = mergeType.OuterSideJoinColumns;
|
||||
}
|
||||
|
||||
public ColumnReferenceType[] InnerSideJoinColumns
|
||||
{
|
||||
get { return this.innerSideJoinColumnsField; }
|
||||
}
|
||||
|
||||
public ColumnReferenceType[] OuterSideJoinColumns
|
||||
{
|
||||
get { return this.outerSideJoinColumnsField; }
|
||||
}
|
||||
|
||||
private ColumnReferenceType[] innerSideJoinColumnsField;
|
||||
private ColumnReferenceType[] outerSideJoinColumnsField;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
//
|
||||
// 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;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Status of operator
|
||||
/// Based on the query profile DMV view
|
||||
/// Pending: when FirstRowTime > 0, Running: FirstRowTime > 0 && CloseTime ==0, Finish: CloseTime > 0
|
||||
/// </summary>
|
||||
public enum STATUS
|
||||
{
|
||||
PENDING,
|
||||
RUNNING,
|
||||
FINISH
|
||||
}
|
||||
|
||||
public class Node
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
public Node(int id, NodeBuilderContext context)
|
||||
{
|
||||
this.ID = id;
|
||||
this.properties = new PropertyDescriptorCollection(new PropertyDescriptor[0]);
|
||||
this.children = new LinkedList<Node>();
|
||||
this.childrenEdges = new LinkedList<Edge>();
|
||||
this.LogicalOpUnlocName = null;
|
||||
this.PhysicalOpUnlocName = null;
|
||||
this.root = context.Graph.Root;
|
||||
if(this.root == null)
|
||||
{
|
||||
this.root = this;
|
||||
}
|
||||
this.Graph = context.Graph;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods and properties
|
||||
|
||||
public int ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public int GroupIndex
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cost associated with the current Node.
|
||||
/// </summary>
|
||||
public double Cost
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.costCalculated)
|
||||
{
|
||||
this.cost = this.SubtreeCost;
|
||||
foreach (Node childNode in this.Children)
|
||||
{
|
||||
this.cost -= childNode.SubtreeCost;
|
||||
}
|
||||
|
||||
// In some cases cost may become a small negative
|
||||
// number due to rounding. Make it 0 in that case.
|
||||
this.cost = Math.Max(this.cost, 0.0);
|
||||
this.costCalculated = true;
|
||||
}
|
||||
|
||||
return this.cost;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative cost associated with the current Node.
|
||||
/// </summary>
|
||||
public double RelativeCost
|
||||
{
|
||||
get
|
||||
{
|
||||
double overallCost = Root.SubtreeCost;
|
||||
return overallCost > 0 ? Cost / overallCost : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cost associated with the Node subtree.
|
||||
/// </summary>
|
||||
public double SubtreeCost
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.subtreeCost == 0)
|
||||
{
|
||||
foreach (Node childNode in this.Children)
|
||||
{
|
||||
this.subtreeCost += childNode.SubtreeCost;
|
||||
}
|
||||
}
|
||||
|
||||
return this.subtreeCost;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.subtreeCost = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operation information (localized name, description, image, etc)
|
||||
/// </summary>
|
||||
public Operation Operation
|
||||
{
|
||||
get { return this.operation; }
|
||||
set { this.operation = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets node properties.
|
||||
/// </summary>
|
||||
public PropertyDescriptorCollection Properties
|
||||
{
|
||||
get { return this.properties; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets node property value.
|
||||
/// </summary>
|
||||
public object this[string propertyName]
|
||||
{
|
||||
get
|
||||
{
|
||||
PropertyValue property = this.properties[propertyName] as PropertyValue;
|
||||
return property != null ? property.Value : null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
PropertyValue property = this.properties[propertyName] as PropertyValue;
|
||||
if (property != null)
|
||||
{
|
||||
// Overwrite existing property value
|
||||
property.Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new property
|
||||
this.properties.Add(PropertyFactory.CreateProperty(propertyName, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsComputeScalarType()
|
||||
{
|
||||
return this[NodeBuilderConstants.PhysicalOp] != null
|
||||
&& ((string)this[NodeBuilderConstants.PhysicalOp]).StartsWith(SR.Keys.ComputeScalar, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool IsSeekOrScanType()
|
||||
{
|
||||
return this[NodeBuilderConstants.PhysicalOp] != null && SeekOrScanPhysicalOpList.Contains(this.PhysicalOpUnlocName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets collection of node children.
|
||||
/// </summary>
|
||||
public LinkedList<Node> Children
|
||||
{
|
||||
get { return this.children; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets current node parent.
|
||||
/// </summary>
|
||||
public Node Parent
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Node Root
|
||||
{
|
||||
get { return this.root; }
|
||||
}
|
||||
|
||||
public Graph Graph
|
||||
{
|
||||
get => this.graph;
|
||||
set
|
||||
{
|
||||
this.graph = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies if this node is finished executing
|
||||
/// </summary>
|
||||
/// <returns>True if finished</returns>
|
||||
public bool IsFinished()
|
||||
{
|
||||
var statusObject = this[NodeBuilderConstants.Status] as STATUS?;
|
||||
|
||||
return statusObject != null && (STATUS)statusObject == STATUS.FINISH;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies if this node is executing
|
||||
/// </summary>
|
||||
/// <returns>True if running</returns>
|
||||
public bool IsRunning()
|
||||
{
|
||||
var statusObject = this[NodeBuilderConstants.Status] as STATUS?;
|
||||
|
||||
return statusObject != null && (STATUS)statusObject == STATUS.RUNNING;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the properties of two nodes are logically similar enough to be considered
|
||||
/// the same for skeleton comparison purposes
|
||||
/// Does not check children
|
||||
/// </summary>
|
||||
/// <param name="nodeToCompare"></param>
|
||||
/// <param name="ignoreDatabaseName"></param>
|
||||
/// <returns></returns>
|
||||
///
|
||||
public bool IsLogicallyEquivalentTo(Node nodeToCompare, bool ignoreDatabaseName)
|
||||
{
|
||||
// same exact node
|
||||
if (this == nodeToCompare)
|
||||
return true;
|
||||
|
||||
// seek and scan types are equivalent so ignore them when comparing logical op
|
||||
if (this[NodeBuilderConstants.LogicalOp] != nodeToCompare[NodeBuilderConstants.LogicalOp] &&
|
||||
(!this.IsSeekOrScanType() || !nodeToCompare.IsSeekOrScanType()))
|
||||
return false;
|
||||
|
||||
// one has object but other does not
|
||||
if (this[objectProperty] != null && nodeToCompare[objectProperty] == null || nodeToCompare[objectProperty] != null && this[objectProperty] == null)
|
||||
return false;
|
||||
|
||||
// both have object
|
||||
if (this[objectProperty] != null && nodeToCompare[objectProperty] != null)
|
||||
{
|
||||
ExpandableObjectWrapper objectProp1 = (ExpandableObjectWrapper)this[objectProperty];
|
||||
ExpandableObjectWrapper objectProp2 = (ExpandableObjectWrapper)nodeToCompare[objectProperty];
|
||||
// object property doesn't match
|
||||
// by default, we ignore DB name
|
||||
// for ex: "[master].[sys].[sysobjvalues].[clst] [e]" and "[master_copy].[sys].[sysobjvalues].[clst] [e]" would be same
|
||||
if (ignoreDatabaseName)
|
||||
{
|
||||
if (!CompareObjectPropertyValue((PropertyValue)(objectProp1.Properties[SR.ObjectServer]), (PropertyValue)(objectProp2.Properties[SR.ObjectServer])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CompareObjectPropertyValue((PropertyValue)(objectProp1.Properties[SR.ObjectSchema]), (PropertyValue)(objectProp2.Properties[SR.ObjectSchema])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CompareObjectPropertyValue((PropertyValue)(objectProp1.Properties[SR.ObjectTable]), (PropertyValue)(objectProp2.Properties[SR.ObjectTable])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CompareObjectPropertyValue((PropertyValue)(objectProp1.Properties[SR.ObjectAlias]), (PropertyValue)(objectProp2.Properties[SR.ObjectAlias])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for CloneAccessScope if it is specified
|
||||
PropertyValue specified1 = (PropertyValue)(objectProp1.Properties["CloneAccessScopeSpecified"]);
|
||||
PropertyValue specified2 = (PropertyValue)(objectProp2.Properties["CloneAccessScopeSpecified"]);
|
||||
if (specified1 == null && specified2 != null || specified1 != null && specified2 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (specified1 != null && specified2 != null)
|
||||
{
|
||||
if ((bool)(specified1.Value) != (bool)(specified2.Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((bool)(specified1.Value) == true)
|
||||
{
|
||||
PropertyValue p1 = (PropertyValue)(objectProp1.Properties["CloneAccessScope"]);
|
||||
PropertyValue p2 = (PropertyValue)(objectProp2.Properties["CloneAccessScope"]);
|
||||
if (p1 == null && p2 != null || p1 != null && p2 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (p1 != null && p2 != null)
|
||||
{
|
||||
if ((CloneAccessScopeType)(p1.Value) != (CloneAccessScopeType)(p2.Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (objectProp1.DisplayName != objectProp2.DisplayName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// same logical op, no other criteria
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ENU name for Logical Operator
|
||||
/// </summary>
|
||||
public string LogicalOpUnlocName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ENU name for Physical Operator
|
||||
/// </summary>
|
||||
public string PhysicalOpUnlocName { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation details
|
||||
|
||||
/// <summary>
|
||||
/// used to compare multiple string type PropertyValue in Object properties,
|
||||
/// for ex: Server, Database, Schema, Table, Index, etc...
|
||||
/// </summary>
|
||||
/// <returns>True if two PropertyValue are equal</returns>
|
||||
private bool CompareObjectPropertyValue(PropertyValue p1, PropertyValue p2)
|
||||
{
|
||||
if (p1 == null && p2 != null || p1 != null && p2 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (p1 != null && p2 != null)
|
||||
{
|
||||
string s1 = p1.Value as string;
|
||||
string s2 = p2.Value as string;
|
||||
if (string.Compare(s1, s2, StringComparison.Ordinal) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
private double cost;
|
||||
private bool costCalculated;
|
||||
private double subtreeCost;
|
||||
private Operation operation;
|
||||
private PropertyDescriptorCollection properties;
|
||||
private LinkedList<Node> children;
|
||||
private readonly string objectProperty = NodeBuilderConstants.Object;
|
||||
private readonly string predicateProperty = NodeBuilderConstants.LogicalOp;
|
||||
private Node parent;
|
||||
private Graph graph;
|
||||
private Edge parentEdge;
|
||||
private LinkedList<Edge> childrenEdges;
|
||||
private string nodeType;
|
||||
|
||||
private Node root;
|
||||
|
||||
/// <summary>
|
||||
/// List of Seek or Scan type operators that can be considered match
|
||||
/// </summary>
|
||||
private List<string> SeekOrScanPhysicalOpList = new List<string> { "IndexSeek", "TableScan", "IndexScan", "ColumnstoreIndexScan" };
|
||||
|
||||
#endregion
|
||||
|
||||
public void AddChild(Node child)
|
||||
{
|
||||
Edge edge = new Edge(this, child);
|
||||
this.childrenEdges.AddLast(edge);
|
||||
child.parentEdge = edge;
|
||||
this.children.AddLast(child);
|
||||
child.parent = this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public class NodeBuilderContext
|
||||
{
|
||||
public NodeBuilderContext(ShowPlanGraph graph, ShowPlanType type, object context)
|
||||
{
|
||||
this.graph = graph;
|
||||
this.showPlanType = type;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets currently processing Graph
|
||||
/// </summary>
|
||||
public ShowPlanGraph Graph
|
||||
{
|
||||
get { return this.graph; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets current ShowPlan type.
|
||||
/// </summary>
|
||||
public ShowPlanType ShowPlanType
|
||||
{
|
||||
get { return this.showPlanType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Misc context object.
|
||||
/// </summary>
|
||||
public object Context
|
||||
{
|
||||
get { return this.context; }
|
||||
}
|
||||
|
||||
private ShowPlanGraph graph;
|
||||
private ShowPlanType showPlanType;
|
||||
private object context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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.Data;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that creates concrete INodeBuilder instances.
|
||||
/// </summary>
|
||||
public static class NodeBuilderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a concrete node builder based on dataSource type
|
||||
/// </summary>
|
||||
/// <param name="dataSource">data</param>
|
||||
/// <returns></returns>
|
||||
public static INodeBuilder Create(object dataSource, ShowPlanType type)
|
||||
{
|
||||
if (dataSource is String || dataSource is byte[] || dataSource is ShowPlanXML)
|
||||
{
|
||||
// REVIEW: add the code that looks inside the XML
|
||||
// and validates the root node and namespace
|
||||
// REVIEW: consider using XmlTextReader
|
||||
return new XmlPlanNodeBuilder(type);
|
||||
}
|
||||
else if (dataSource is IDataReader)
|
||||
{
|
||||
// REVIEW: for now the assumption is that this is
|
||||
// a Shiloh Row set, either actual or estimated
|
||||
if (type == ShowPlanType.Actual)
|
||||
{
|
||||
return new ActualPlanDataReaderNodeBuilder();
|
||||
}
|
||||
else if (type == ShowPlanType.Estimated)
|
||||
{
|
||||
return new EstimatedPlanDataReaderNodeBuilder();
|
||||
}
|
||||
// else if (type == ShowPlanType.Live)
|
||||
// {
|
||||
// return new LivePlanDataReaderNodeBuilder();
|
||||
// }
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Unexpected ShowPlan type");
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(false, "Unexpected ShowPlan source = " + dataSource.ToString());
|
||||
throw new ArgumentException(SR.Keys.UnknownShowPlanSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all object / Node parsers
|
||||
/// Used for parsing properties and hierarchy.
|
||||
/// </summary>
|
||||
public abstract class ObjectParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses item properties.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">Item which properties are being parsed.</param>
|
||||
/// <param name="targetPropertyBag">Target property bag to populate with property wrappers.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
public virtual void ParseProperties(object parsedItem, PropertyDescriptorCollection targetPropertyBag, NodeBuilderContext context)
|
||||
{
|
||||
PropertyDescriptorCollection allProperties = TypeDescriptor.GetProperties(parsedItem);
|
||||
|
||||
|
||||
|
||||
foreach (PropertyDescriptor property in allProperties)
|
||||
{
|
||||
if (property.Attributes.Contains(XmlIgnoreAttribute))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// a special "...Specified" property (such as StatementIdSpecified)
|
||||
PropertyDescriptor specifiedProperty = allProperties[property.Name + "Specified"];
|
||||
if (specifiedProperty != null && specifiedProperty.GetValue(parsedItem).Equals(false))
|
||||
{
|
||||
// The "...Specified" property value is false.
|
||||
// We should skip this property
|
||||
continue;
|
||||
}
|
||||
|
||||
object value = property.GetValue(parsedItem);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Type.GetTypeCode(property.PropertyType) == TypeCode.Object && ShouldSkipProperty(property))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// In case of xml Choice group, the property name can be general like "Item" or "Items".
|
||||
// Ideally, it should contain/refer to only one of the possible values in xml choice group,
|
||||
// but due to limitations on engine side the xml choice group can contain more than one
|
||||
// value and it is not possible to change the choice group to a sequence group in XSD
|
||||
// because engine is not able to generate values in a particular order for the case of
|
||||
// warnings type. Hence, we need to iterate through all the values in items and create
|
||||
// separate property for each value and add it to target property bag.
|
||||
if (property.Name == "Items" || property.Name == "Item")
|
||||
{
|
||||
ICollection collection = value as ICollection;
|
||||
if (collection != null)
|
||||
{
|
||||
foreach (object obj in collection)
|
||||
{
|
||||
ObjectParser.AddProperty(targetPropertyBag, property, obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//We can get single object in choice like SeekPredicateNew in Spool as Item
|
||||
ObjectParser.AddProperty(targetPropertyBag, property, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectParser.AddProperty(targetPropertyBag, property, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddProperty(PropertyDescriptorCollection targetPropertyBag, PropertyDescriptor property, object value)
|
||||
{
|
||||
PropertyDescriptor wrapperProperty = PropertyFactory.CreateProperty(property, value);
|
||||
if (wrapperProperty != null)
|
||||
{
|
||||
targetPropertyBag.Add(wrapperProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the current property is used to reference a child item.
|
||||
/// Hierarchy properties are skipped when property wrappers are being created.
|
||||
/// </summary>
|
||||
/// <param name="property">Property subject to test.</param>
|
||||
/// <returns>True if the property is a hierarchy property;
|
||||
/// false if this is a regular property that should appear in the property grid.
|
||||
/// </returns>
|
||||
protected virtual bool ShouldSkipProperty(PropertyDescriptor property)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly Attribute XmlIgnoreAttribute = new System.Xml.Serialization.XmlIgnoreAttribute();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public sealed class Operation
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs Operation.
|
||||
/// </summary>
|
||||
/// <param name="name">Operator name</param>
|
||||
/// <param name="displayNameKey">Display name resource ID</param>
|
||||
/// <param name="descriptionKey">Description resource ID</param>
|
||||
/// <param name="imageName">Image name</param>
|
||||
public Operation(string name, string displayNameKey, string descriptionKey, string imageName)
|
||||
{
|
||||
this.name = name;
|
||||
this.displayNameKey = displayNameKey;
|
||||
this.descriptionKey = descriptionKey;
|
||||
this.imageName = imageName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="name">Operator name</param>
|
||||
/// <param name="displayNameKey">Display name resource ID</param>
|
||||
/// <returns></returns>
|
||||
public Operation(string name, string displayNameKey): this(name, displayNameKey, null, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets operator name.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return this.name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets localized display name.
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.displayName == null && this.displayNameKey != null)
|
||||
{
|
||||
this.displayName = SR.Keys.GetString(this.displayNameKey);
|
||||
Debug.Assert(this.displayName != null);
|
||||
}
|
||||
|
||||
return this.displayName != null ? this.displayName : this.name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets localized description.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.description == null && this.descriptionKey != null)
|
||||
{
|
||||
this.description = SR.Keys.GetString(this.descriptionKey);
|
||||
Debug.Assert(this.description != null);
|
||||
}
|
||||
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets image.
|
||||
/// </summary>
|
||||
public string Image
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.image == null && this.imageName != null)
|
||||
{
|
||||
this.image = this.imageName;
|
||||
}
|
||||
return this.image;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates one-off operation with only display name.
|
||||
/// </summary>
|
||||
/// <param name="operationDisplayName">Operation display name.</param>
|
||||
public static Operation CreateUnknown(string operationDisplayName, string iconName)
|
||||
{
|
||||
return new Operation(operationDisplayName, null, null, iconName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unknown operation
|
||||
/// </summary>
|
||||
public static Operation Unknown
|
||||
{
|
||||
get { return Operation.unknown; }
|
||||
}
|
||||
|
||||
private string name;
|
||||
private string displayNameKey;
|
||||
private string descriptionKey;
|
||||
private string imageName;
|
||||
private string helpKeyword;
|
||||
private Type displayNodeType;
|
||||
|
||||
private string image;
|
||||
private string displayName;
|
||||
private string description;
|
||||
|
||||
private static readonly Operation unknown = new Operation(
|
||||
String.Empty,
|
||||
SR.Keys.Unknown,
|
||||
SR.Keys.UnknownDescription,
|
||||
"Result_32x.ico");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that holds information about a physical or logical operator, or a statement.
|
||||
/// </summary>
|
||||
internal static class OperationTable
|
||||
{
|
||||
#region Public members
|
||||
|
||||
public static Operation GetStatement(string statementTypeName)
|
||||
{
|
||||
Operation operation;
|
||||
|
||||
if (!Statements.TryGetValue(statementTypeName, out operation))
|
||||
{
|
||||
operation = Operation.CreateUnknown(statementTypeName, "Language_construct_catch_all.ico");
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
public static Operation GetCursorType(string cursorTypeName)
|
||||
{
|
||||
Operation operation;
|
||||
|
||||
if (!CursorTypes.TryGetValue(cursorTypeName, out operation))
|
||||
{
|
||||
cursorTypeName = GetNameFromXmlEnumAttribute(cursorTypeName, typeof(CursorType));
|
||||
operation = Operation.CreateUnknown(cursorTypeName, "Cursor_catch_all_32x.ico");
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
public static Operation GetPhysicalOperation(string operationType)
|
||||
{
|
||||
Operation operation;
|
||||
|
||||
if (!PhysicalOperations.TryGetValue(operationType, out operation))
|
||||
{
|
||||
operationType = GetNameFromXmlEnumAttribute(operationType, typeof(PhysicalOpType));
|
||||
operation = Operation.CreateUnknown(operationType, "Iterator_catch_all.ico");
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
public static Operation GetLogicalOperation(string operationType)
|
||||
{
|
||||
Operation operation;
|
||||
|
||||
if (!LogicalOperations.TryGetValue(operationType, out operation))
|
||||
{
|
||||
operationType = GetNameFromXmlEnumAttribute(operationType, typeof(LogicalOpType));
|
||||
// Should not use Operation.CreateUnknown here, because it would
|
||||
// use some default description and icons. Instead we should fall back to description
|
||||
// and Icon from the corresponding physical operation.
|
||||
operation = new Operation(null, operationType);
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
public static Operation GetUdf()
|
||||
{
|
||||
return new Operation(null, SR.Keys.Udf, null, "Language_construct_catch_all.ico");
|
||||
}
|
||||
|
||||
public static Operation GetStoredProc()
|
||||
{
|
||||
return new Operation(null, SR.Keys.StoredProc, null, "Language_construct_catch_all.ico");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation details
|
||||
|
||||
static OperationTable()
|
||||
{
|
||||
Operation[] physicalOperationList = new Operation[]
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// XML ShowPlan Operators (see showplanxml.cs for the list)
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new Operation("AdaptiveJoin", SR.Keys.AdaptiveJoin, SR.Keys.AdaptiveJoinDescription, "Adaptive_Join_32x.ico"),
|
||||
new Operation("Assert", SR.Keys.Assert, SR.Keys.AssertDescription, "Assert_32x.ico"),
|
||||
new Operation("Bitmap", SR.Keys.Bitmap, SR.Keys.BitmapDescription, "Bitmap_32x.ico"),
|
||||
new Operation("ClusteredIndexDelete", SR.Keys.ClusteredIndexDelete, SR.Keys.ClusteredIndexDeleteDescription, "Clustered_index_delete_32x.ico"),
|
||||
new Operation("ClusteredIndexInsert", SR.Keys.ClusteredIndexInsert, SR.Keys.ClusteredIndexInsertDescription, "Clustered_index_insert_32x.ico"),
|
||||
new Operation("ClusteredIndexScan", SR.Keys.ClusteredIndexScan, SR.Keys.ClusteredIndexScanDescription, "Clustered_index_scan_32x.ico"),
|
||||
new Operation("ClusteredIndexSeek", SR.Keys.ClusteredIndexSeek, SR.Keys.ClusteredIndexSeekDescription, "Clustered_index_seek_32x.ico"),
|
||||
new Operation("ClusteredIndexUpdate", SR.Keys.ClusteredIndexUpdate, SR.Keys.ClusteredIndexUpdateDescription, "Clustered_index_update_32x.ico"),
|
||||
new Operation("ClusteredIndexMerge", SR.Keys.ClusteredIndexMerge, SR.Keys.ClusteredIndexMergeDescription, "Clustered_index_merge_32x.ico"),
|
||||
new Operation("ClusteredUpdate", SR.Keys.ClusteredUpdate, SR.Keys.ClusteredUpdateDescription, "Clustered_update_32x.ico"),
|
||||
new Operation("Collapse", SR.Keys.Collapse, SR.Keys.CollapseDescription, "Collapse_32x.ico"),
|
||||
new Operation("ComputeScalar", SR.Keys.ComputeScalar, SR.Keys.ComputeScalarDescription, "Compute_scalar_32x.ico"),
|
||||
new Operation("Concatenation", SR.Keys.Concatenation, SR.Keys.ConcatenationDescription, "Concatenation_32x.ico"),
|
||||
new Operation("ConstantScan", SR.Keys.ConstantScan, SR.Keys.ConstantScanDescription, "Constant_scan_32x.ico"),
|
||||
new Operation("DeletedScan", SR.Keys.DeletedScan, SR.Keys.DeletedScanDescription, "Deleted_scan_32x.ico"),
|
||||
new Operation("Filter", SR.Keys.Filter, SR.Keys.FilterDescription, "Filter_32x.ico"),
|
||||
new Operation("HashMatch", SR.Keys.HashMatch, SR.Keys.HashMatchDescription, "Hash_match_32x.ico"),
|
||||
new Operation("IndexDelete", SR.Keys.IndexDelete, SR.Keys.IndexDeleteDescription, "Nonclust_index_delete_32x.ico"),
|
||||
new Operation("IndexInsert", SR.Keys.IndexInsert, SR.Keys.IndexInsertDescription, "Nonclust_index_insert_32x.ico"),
|
||||
new Operation("IndexScan", SR.Keys.IndexScan, SR.Keys.IndexScanDescription, "Nonclust_index_scan_32x.ico"),
|
||||
new Operation("ColumnstoreIndexDelete", SR.Keys.ColumnstoreIndexDelete, SR.Keys.ColumnstoreIndexDeleteDescription, "Columnstore_index_delete_32x.ico"),
|
||||
new Operation("ColumnstoreIndexInsert", SR.Keys.ColumnstoreIndexInsert, SR.Keys.ColumnstoreIndexInsertDescription, "Columnstore_index_insert_32x.ico"),
|
||||
new Operation("ColumnstoreIndexMerge", SR.Keys.ColumnstoreIndexMerge, SR.Keys.ColumnstoreIndexMergeDescription, "Columnstore_index_merge_32x.ico"),
|
||||
new Operation("ColumnstoreIndexScan", SR.Keys.ColumnstoreIndexScan, SR.Keys.ColumnstoreIndexScanDescription, "Columnstore_index_scan_32x.ico"),
|
||||
new Operation("ColumnstoreIndexUpdate", SR.Keys.ColumnstoreIndexUpdate, SR.Keys.ColumnstoreIndexUpdateDescription, "Columnstore_index_update_32x.ico"),
|
||||
new Operation("IndexSeek", SR.Keys.IndexSeek, SR.Keys.IndexSeekDescription, "Nonclust_index_seek_32x.ico"),
|
||||
new Operation("IndexSpool", SR.Keys.IndexSpool, SR.Keys.IndexSpoolDescription, "Nonclust_index_spool_32x.ico"),
|
||||
new Operation("IndexUpdate", SR.Keys.IndexUpdate, SR.Keys.IndexUpdateDescription, "Nonclust_index_update_32x.ico"),
|
||||
new Operation("InsertedScan", SR.Keys.InsertedScan, SR.Keys.InsertedScanDescription, "Inserted_scan_32x.ico"),
|
||||
new Operation("LogRowScan", SR.Keys.LogRowScan, SR.Keys.LogRowScanDescription, "Log_row_scan_32x.ico"),
|
||||
new Operation("MergeInterval", SR.Keys.MergeInterval, SR.Keys.MergeIntervalDescription, "Merge_interval_32x.ico"),
|
||||
new Operation("MergeJoin", SR.Keys.MergeJoin, SR.Keys.MergeJoinDescription, "Merge_join_32x.ico"),
|
||||
new Operation("NestedLoops", SR.Keys.NestedLoops, SR.Keys.NestedLoopsDescription, "Nested_loops_32x.ico"),
|
||||
new Operation("Parallelism", SR.Keys.Parallelism, SR.Keys.ParallelismDescription, "Parallelism_32x.ico"),
|
||||
new Operation("ParameterTableScan", SR.Keys.ParameterTableScan, SR.Keys.ParameterTableScanDescription, "Parameter_table_scan_32x.ico"),
|
||||
new Operation("Print", SR.Keys.Print, SR.Keys.PrintDescription, "Print.ico"),
|
||||
new Operation("Put", SR.Keys.Put, SR.Keys.PutDescription, "Put_32x.ico"),
|
||||
new Operation("Rank", SR.Keys.Rank, SR.Keys.RankDescription, "Rank_32x.ico"),
|
||||
// using the temporary icon as of now. Once the new icon is available, it will be updated.
|
||||
new Operation("ForeignKeyReferencesCheck", SR.Keys.ForeignKeyReferencesCheck, SR.Keys.ForeignKeyReferencesCheckDescription, "Referential_Integrity_32x.ico"),
|
||||
new Operation("RemoteDelete", SR.Keys.RemoteDelete, SR.Keys.RemoteDeleteDescription, "Remote_delete_32x.ico"),
|
||||
new Operation("RemoteIndexScan", SR.Keys.RemoteIndexScan, SR.Keys.RemoteIndexScanDescription, "Remote_index_scan_32x.ico"),
|
||||
new Operation("RemoteIndexSeek", SR.Keys.RemoteIndexSeek, SR.Keys.RemoteIndexSeekDescription, "Remote_index_seek_32x.ico"),
|
||||
new Operation("RemoteInsert", SR.Keys.RemoteInsert, SR.Keys.RemoteInsertDescription, "Remote_insert_32x.ico"),
|
||||
new Operation("RemoteQuery", SR.Keys.RemoteQuery, SR.Keys.RemoteQueryDescription, "Remote_query_32x.ico"),
|
||||
new Operation("RemoteScan", SR.Keys.RemoteScan, SR.Keys.RemoteScanDescription, "Remote_scan_32x.ico"),
|
||||
new Operation("RemoteUpdate", SR.Keys.RemoteUpdate, SR.Keys.RemoteUpdateDescription, "Remote_update_32x.ico"),
|
||||
new Operation("RIDLookup", SR.Keys.RIDLookup, SR.Keys.RIDLookupDescription, "RID_clustered_locate_32x.ico"),
|
||||
new Operation("RowCountSpool", SR.Keys.RowCountSpool, SR.Keys.RowCountSpoolDescription, "Remote_count_spool_32x.ico"),
|
||||
new Operation("Segment", SR.Keys.Segment, SR.Keys.SegmentDescription, "Segment_32x.ico"),
|
||||
new Operation("Sequence", SR.Keys.Sequence, SR.Keys.SequenceDescription, "Sequence_32x.ico"),
|
||||
new Operation("SequenceProject", SR.Keys.SequenceProject, SR.Keys.SequenceProjectDescription, "Sequence_project_32x.ico"),
|
||||
new Operation("Sort", SR.Keys.Sort, SR.Keys.SortDescription, "Sort_32x.ico"),
|
||||
new Operation("Split", SR.Keys.Split, SR.Keys.SplitDescription, "Split_32x.ico"),
|
||||
new Operation("StreamAggregate", SR.Keys.StreamAggregate, SR.Keys.StreamAggregateDescription, "Stream_aggregate_32x.ico"),
|
||||
new Operation("Switch", SR.Keys.Switch, SR.Keys.SwitchDescription, "Switch_32x.ico"),
|
||||
new Operation("Tablevaluedfunction", SR.Keys.TableValueFunction, SR.Keys.TableValueFunctionDescription, "Table_value_function_32x.ico"),
|
||||
new Operation("TableDelete", SR.Keys.TableDelete, SR.Keys.TableDeleteDescription, "Table_delete_32x.ico"),
|
||||
new Operation("TableInsert", SR.Keys.TableInsert, SR.Keys.TableInsertDescription, "Table_insert_32x.ico"),
|
||||
new Operation("TableScan", SR.Keys.TableScan, SR.Keys.TableScanDescription, "Table_scan_32x.ico"),
|
||||
new Operation("TableSpool", SR.Keys.TableSpool, SR.Keys.TableSpoolDescription, "Table_spool_32x.ico"),
|
||||
new Operation("TableUpdate", SR.Keys.TableUpdate, SR.Keys.TableUpdateDescription, "Table_update_32x.ico"),
|
||||
new Operation("TableMerge", SR.Keys.TableMerge, SR.Keys.TableMergeDescription, "Table_merge_32x.ico"),
|
||||
new Operation("TFP", SR.Keys.TFP, SR.Keys.TFPDescription, "Predict_32x.ico"),
|
||||
new Operation("Top", SR.Keys.Top, SR.Keys.TopDescription, "Top_32x.ico"),
|
||||
new Operation("UDX", SR.Keys.UDX, SR.Keys.UDXDescription, "UDX_32x.ico"),
|
||||
new Operation("BatchHashTableBuild", SR.Keys.BatchHashTableBuild, SR.Keys.BatchHashTableBuildDescription, "BatchHashTableBuild_32x.ico"),
|
||||
new Operation("WindowSpool", SR.Keys.Window, SR.Keys.WindowDescription, "Table_spool_32x.ico"),
|
||||
new Operation("WindowAggregate", SR.Keys.WindowAggregate, SR.Keys.WindowAggregateDescription, "Window_aggregate_32x.ico"),
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// XML ShowPlan Cursor Operators (see showplanxml.cs for the list)
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
new Operation("FetchQuery", SR.Keys.FetchQuery, SR.Keys.FetchQueryDescription, "Fetch_query_32x.ico"),
|
||||
new Operation("PopulateQuery", SR.Keys.PopulationQuery, SR.Keys.PopulationQueryDescription, "Population_query_32x.ico"),
|
||||
new Operation("RefreshQuery", SR.Keys.RefreshQuery, SR.Keys.RefreshQueryDescription, "Refresh_query_32x.ico"),
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Shiloh Operators (see star\sqlquery\src\plan.cpp for the list)
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new Operation("Result", SR.Keys.Result, SR.Keys.ResultDescription, "Result_32x.ico"),
|
||||
new Operation("Aggregate", SR.Keys.Aggregate, SR.Keys.AggregateDescription, "Aggregate_32x.ico"),
|
||||
new Operation("Assign", SR.Keys.Assign, SR.Keys.AssignDescription, "Assign_32x.ico"),
|
||||
new Operation("ArithmeticExpression", SR.Keys.ArithmeticExpression, SR.Keys.ArithmeticExpressionDescription, "Arithmetic_expression_32x.ico"),
|
||||
new Operation("BookmarkLookup", SR.Keys.BookmarkLookup, SR.Keys.BookmarkLookupDescription, "Bookmark_lookup_32x.ico"),
|
||||
new Operation("Convert", SR.Keys.Convert, SR.Keys.ConvertDescription, "Convert_32x.ico"),
|
||||
new Operation("Declare", SR.Keys.Declare, SR.Keys.DeclareDescription, "Declare_32x.ico"),
|
||||
new Operation("Delete", SR.Keys.Delete, SR.Keys.DeleteDescription, "Delete_32x.ico"),
|
||||
new Operation("Dynamic", SR.Keys.Dynamic, SR.Keys.DynamicDescription, "Dynamic_32x.ico"),
|
||||
new Operation("HashMatchRoot", SR.Keys.HashMatchRoot, SR.Keys.HashMatchRootDescription, "Hash_match_root_32x.ico"),
|
||||
new Operation("HashMatchTeam", SR.Keys.HashMatchTeam, SR.Keys.HashMatchTeamDescription, "Hash_match_team_32x.ico"),
|
||||
new Operation("If", SR.Keys.If, SR.Keys.IfDescription, "If_32x.ico"),
|
||||
new Operation("Insert", SR.Keys.Insert, SR.Keys.InsertDescription, "Insert_32x.ico"),
|
||||
new Operation("Intrinsic", SR.Keys.Intrinsic, SR.Keys.IntrinsicDescription, "Intrinsic_32x.ico"),
|
||||
new Operation("Keyset", SR.Keys.Keyset, SR.Keys.KeysetDescription, "Keyset_32x.ico"),
|
||||
new Operation("Locate", SR.Keys.Locate, SR.Keys.LocateDescription, "RID_nonclustered_locate_32x.ico"),
|
||||
new Operation("PopulationQuery", SR.Keys.PopulationQuery, SR.Keys.PopulationQueryDescription, "Population_query_32x.ico"),
|
||||
new Operation("SetFunction", SR.Keys.SetFunction, SR.Keys.SetFunctionDescription, "Set_function_32x.ico"),
|
||||
new Operation("Snapshot", SR.Keys.Snapshot, SR.Keys.SnapshotDescription, "Snapshot_32x.ico"),
|
||||
new Operation("Spool", SR.Keys.Spool, SR.Keys.SpoolDescription, "Spool_32x.ico"),
|
||||
new Operation("TSQL", SR.Keys.SQL, SR.Keys.SQLDescription, "SQL_32x.ico"),
|
||||
new Operation("Update", SR.Keys.Update, SR.Keys.UpdateDescription, "Update_32x.ico"),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Fake Operators - Used to special case existing operators and expose them using different name / icons (see sqlbu#434739)
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new Operation("KeyLookup", SR.Keys.KeyLookup, SR.Keys.KeyLookupDescription, "Bookmark_lookup_32x.ico"),
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// PDW Operators (See PDW comment tags in showplanxml.xsd)
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new Operation("Apply", SR.Keys.Apply, SR.Keys.ApplyDescription, "Apply_32x.ico"),
|
||||
new Operation("Broadcast", SR.Keys.Broadcast, SR.Keys.BroadcastDescription, "Broadcast_32x.ico"),
|
||||
new Operation("ComputeToControlNode", SR.Keys.ComputeToControlNode, SR.Keys.ComputeToControlNodeDescription, "Compute_to_control_32x.ico"),
|
||||
new Operation("ConstTableGet", SR.Keys.ConstTableGet, SR.Keys.ConstTableGetDescription, "Const_table_get_32x.ico"),
|
||||
new Operation("ControlToComputeNodes", SR.Keys.ControlToComputeNodes, SR.Keys.ControlToComputeNodesDescription, "Control_to_compute_32x.ico"),
|
||||
new Operation("ExternalBroadcast", SR.Keys.ExternalBroadcast, SR.Keys.ExternalBroadcastDescription, "External_broadcast_32x.ico"),
|
||||
new Operation("ExternalExport", SR.Keys.ExternalExport, SR.Keys.ExternalExportDescription, "External_export_32x.ico"),
|
||||
new Operation("ExternalLocalStreaming", SR.Keys.ExternalLocalStreaming, SR.Keys.ExternalLocalStreamingDescription, "External_local_streaming_32x.ico"),
|
||||
new Operation("ExternalRoundRobin", SR.Keys.ExternalRoundRobin, SR.Keys.ExternalRoundRobinDescription, "External_round_robin_32x.ico"),
|
||||
new Operation("ExternalShuffle", SR.Keys.ExternalShuffle, SR.Keys.ExternalShuffleDescription, "External_shuffle_32x.ico"),
|
||||
new Operation("Get", SR.Keys.Get, SR.Keys.GetDescription, "Get_32x.ico"),
|
||||
new Operation("GbApply", SR.Keys.GbApply, SR.Keys.GbApplyDescription, "Apply_32x.ico"),
|
||||
new Operation("GbAgg", SR.Keys.GbAgg, SR.Keys.GbAggDescription, "Group_by_aggregate_32x.ico"),
|
||||
new Operation("Join", SR.Keys.Join, SR.Keys.JoinDescription, "Join_32x.ico"),
|
||||
new Operation("LocalCube", SR.Keys.LocalCube, SR.Keys.LocalCubeDescription, "Intrinsic_32x.ico"),
|
||||
new Operation("Project", SR.Keys.Project, SR.Keys.ProjectDescription, "Project_32x.ico"),
|
||||
new Operation("Shuffle", SR.Keys.Shuffle, SR.Keys.ShuffleDescription, "Shuffle_32x.ico"),
|
||||
new Operation("SingleSourceRoundRobin", SR.Keys.SingleSourceRoundRobin, SR.Keys.SingleSourceRoundRobinDescription, "Single_source_round_robin_32x.ico"),
|
||||
new Operation("SingleSourceShuffle", SR.Keys.SingleSourceShuffle, SR.Keys.SingleSourceShuffleDescription, "Single_source_shuffle_32x.ico"),
|
||||
new Operation("Trim", SR.Keys.Trim, SR.Keys.TrimDescription, "Trim_32x.ico"),
|
||||
new Operation("Union", SR.Keys.Union, SR.Keys.UnionDescription, "Union_32x.ico"),
|
||||
new Operation("UnionAll", SR.Keys.UnionAll, SR.Keys.UnionAllDescription, "Union_all_32x.ico"),
|
||||
};
|
||||
|
||||
PhysicalOperations = DictionaryFromList(physicalOperationList);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Logical Operations
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Operation[] logicalOperationList = new Operation[]
|
||||
{
|
||||
new Operation("Aggregate", SR.Keys.LogicalOpAggregate),
|
||||
new Operation("AntiDiff", SR.Keys.LogicalOpAntiDiff),
|
||||
new Operation("Assert", SR.Keys.LogicalOpAssert),
|
||||
new Operation("BitmapCreate", SR.Keys.LogicalOpBitmapCreate),
|
||||
new Operation("ClusteredIndexScan", SR.Keys.LogicalOpClusteredIndexScan),
|
||||
new Operation("ClusteredIndexSeek", SR.Keys.LogicalOpClusteredIndexSeek),
|
||||
new Operation("ClusteredUpdate", SR.Keys.LogicalOpClusteredUpdate),
|
||||
new Operation("Collapse", SR.Keys.LogicalOpCollapse),
|
||||
new Operation("ComputeScalar", SR.Keys.LogicalOpComputeScalar),
|
||||
new Operation("Concatenation", SR.Keys.LogicalOpConcatenation),
|
||||
new Operation("ConstantScan", SR.Keys.LogicalOpConstantScan),
|
||||
new Operation("CrossJoin", SR.Keys.LogicalOpCrossJoin),
|
||||
new Operation("Delete", SR.Keys.LogicalOpDelete),
|
||||
new Operation("DeletedScan", SR.Keys.LogicalOpDeletedScan),
|
||||
new Operation("DistinctSort", SR.Keys.LogicalOpDistinctSort),
|
||||
new Operation("Distinct", SR.Keys.LogicalOpDistinct),
|
||||
new Operation("DistributeStreams", SR.Keys.LogicalOpDistributeStreams, SR.Keys.DistributeStreamsDescription, "Parallelism_distribute.ico"),
|
||||
new Operation("EagerSpool", SR.Keys.LogicalOpEagerSpool),
|
||||
new Operation("Filter", SR.Keys.LogicalOpFilter),
|
||||
new Operation("FlowDistinct", SR.Keys.LogicalOpFlowDistinct),
|
||||
new Operation("FullOuterJoin", SR.Keys.LogicalOpFullOuterJoin),
|
||||
new Operation("GatherStreams", SR.Keys.LogicalOpGatherStreams, SR.Keys.GatherStreamsDescription, "Parallelism_32x.ico"),
|
||||
new Operation("IndexScan", SR.Keys.LogicalOpIndexScan),
|
||||
new Operation("IndexSeek", SR.Keys.LogicalOpIndexSeek),
|
||||
new Operation("InnerApply", SR.Keys.LogicalOpInnerApply),
|
||||
new Operation("InnerJoin", SR.Keys.LogicalOpInnerJoin),
|
||||
new Operation("Insert", SR.Keys.LogicalOpInsert),
|
||||
new Operation("InsertedScan", SR.Keys.LogicalOpInsertedScan),
|
||||
new Operation("IntersectAll", SR.Keys.LogicalOpIntersectAll),
|
||||
new Operation("Intersect", SR.Keys.LogicalOpIntersect),
|
||||
new Operation("KeyLookup", SR.Keys.LogicalKeyLookup),
|
||||
new Operation("LazySpool", SR.Keys.LogicalOpLazySpool),
|
||||
new Operation("LeftAntiSemiApply", SR.Keys.LogicalOpLeftAntiSemiApply),
|
||||
new Operation("LeftAntiSemiJoin", SR.Keys.LogicalOpLeftAntiSemiJoin),
|
||||
new Operation("LeftDiffAll", SR.Keys.LogicalOpLeftDiffAll),
|
||||
new Operation("LeftDiff", SR.Keys.LogicalOpLeftDiff),
|
||||
new Operation("LeftOuterApply", SR.Keys.LogicalOpLeftOuterApply),
|
||||
new Operation("LeftOuterJoin", SR.Keys.LogicalOpLeftOuterJoin),
|
||||
new Operation("LeftSemiApply", SR.Keys.LogicalOpLeftSemiApply),
|
||||
new Operation("LeftSemiJoin", SR.Keys.LogicalOpLeftSemiJoin),
|
||||
new Operation("LogRowScan", SR.Keys.LogicalOpLogRowScan),
|
||||
new Operation("MergeInterval", SR.Keys.LogicalOpMergeInterval),
|
||||
new Operation("ParameterTableScan", SR.Keys.LogicalOpParameterTableScan),
|
||||
new Operation("PartialAggregate", SR.Keys.LogicalOpPartialAggregate),
|
||||
new Operation("Print", SR.Keys.LogicalOpPrint),
|
||||
new Operation("Put", SR.Keys.LogicalOpPut),
|
||||
new Operation("Rank", SR.Keys.LogicalOpRank),
|
||||
new Operation("ForeignKeyReferencesCheck", SR.Keys.LogicalOpForeignKeyReferencesCheck),
|
||||
new Operation("RemoteDelete", SR.Keys.LogicalOpRemoteDelete),
|
||||
new Operation("RemoteIndexScan", SR.Keys.LogicalOpRemoteIndexScan),
|
||||
new Operation("RemoteIndexSeek", SR.Keys.LogicalOpRemoteIndexSeek),
|
||||
new Operation("RemoteInsert", SR.Keys.LogicalOpRemoteInsert),
|
||||
new Operation("RemoteQuery", SR.Keys.LogicalOpRemoteQuery),
|
||||
new Operation("RemoteScan", SR.Keys.LogicalOpRemoteScan),
|
||||
new Operation("RemoteUpdate", SR.Keys.LogicalOpRemoteUpdate),
|
||||
new Operation("RepartitionStreams", SR.Keys.LogicalOpRepartitionStreams, SR.Keys.RepartitionStreamsDescription, "Parallelism_repartition.ico"),
|
||||
new Operation("RIDLookup", SR.Keys.LogicalOpRIDLookup),
|
||||
new Operation("RightAntiSemiJoin", SR.Keys.LogicalOpRightAntiSemiJoin),
|
||||
new Operation("RightDiffAll", SR.Keys.LogicalOpRightDiffAll),
|
||||
new Operation("RightDiff", SR.Keys.LogicalOpRightDiff),
|
||||
new Operation("RightOuterJoin", SR.Keys.LogicalOpRightOuterJoin),
|
||||
new Operation("RightSemiJoin", SR.Keys.LogicalOpRightSemiJoin),
|
||||
new Operation("Segment", SR.Keys.LogicalOpSegment),
|
||||
new Operation("Sequence", SR.Keys.LogicalOpSequence),
|
||||
new Operation("Sort", SR.Keys.LogicalOpSort),
|
||||
new Operation("Split", SR.Keys.LogicalOpSplit),
|
||||
new Operation("Switch", SR.Keys.LogicalOpSwitch),
|
||||
new Operation("Tablevaluedfunction", SR.Keys.LogicalOpTableValuedFunction),
|
||||
new Operation("TableScan", SR.Keys.LogicalOpTableScan),
|
||||
new Operation("Top", SR.Keys.LogicalOpTop),
|
||||
new Operation("TopNSort", SR.Keys.LogicalOpTopNSort),
|
||||
new Operation("UDX", SR.Keys.LogicalOpUDX),
|
||||
new Operation("Union", SR.Keys.LogicalOpUnion),
|
||||
new Operation("Update", SR.Keys.LogicalOpUpdate),
|
||||
new Operation("Merge", SR.Keys.LogicalOpMerge),
|
||||
new Operation("MergeStats", SR.Keys.LogicalOpMergeStats),
|
||||
new Operation("LocalStats", SR.Keys.LogicalOpLocalStats),
|
||||
new Operation("BatchHashTableBuild", SR.Keys.LogicalOpBatchHashTableBuild),
|
||||
new Operation("WindowSpool", SR.Keys.LogicalOpWindow),
|
||||
};
|
||||
|
||||
LogicalOperations = DictionaryFromList(logicalOperationList);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Statements
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO: may need to put a few more statements in here
|
||||
Operation[] statementList = new Operation[]
|
||||
{
|
||||
new Operation("SELECT", null, null, "Result_32x.ico"),
|
||||
new Operation("COND", null, null, "If_32x.ico")
|
||||
};
|
||||
|
||||
Statements = DictionaryFromList(statementList);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Cursor types
|
||||
/// Name / Type SR Display Name Key SR Description Key Image
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Operation[] cursorTypeList = new Operation[]
|
||||
{
|
||||
new Operation("Dynamic", SR.Keys.Dynamic, SR.Keys.DynamicDescription, "Dynamic_32x.ico"),
|
||||
new Operation("FastForward", SR.Keys.FastForward, SR.Keys.FastForwardDescription, "Cursor_catch_all_32x.ico"),
|
||||
new Operation("Keyset", SR.Keys.Keyset, SR.Keys.KeysetDescription, "Keyset_32x.ico"),
|
||||
new Operation("SnapShot", SR.Keys.Snapshot, SR.Keys.SnapshotDescription, "Snapshot_32x.ico")
|
||||
};
|
||||
|
||||
CursorTypes = DictionaryFromList(cursorTypeList);
|
||||
}
|
||||
|
||||
private static Dictionary<string, Operation> DictionaryFromList(Operation[] list)
|
||||
{
|
||||
Dictionary<string, Operation> dictionary = new Dictionary<string, Operation>(list.Length);
|
||||
foreach (Operation item in list)
|
||||
{
|
||||
dictionary.Add(item.Name, item);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
private static string GetNameFromXmlEnumAttribute(string enumMemberName, Type enumType)
|
||||
{
|
||||
Debug.Assert(enumType.IsEnum);
|
||||
|
||||
foreach (MemberInfo member in enumType.GetMembers())
|
||||
{
|
||||
if (member.Name == enumMemberName)
|
||||
{
|
||||
object[] attributes = member.GetCustomAttributes(typeof(System.Xml.Serialization.XmlEnumAttribute), true);
|
||||
foreach (System.Xml.Serialization.XmlEnumAttribute attribute in attributes)
|
||||
{
|
||||
return attribute.Name;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing has been found, just return enumMemberName.
|
||||
return enumMemberName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private members
|
||||
|
||||
private static readonly Dictionary<string, Operation> PhysicalOperations;
|
||||
private static readonly Dictionary<string, Operation> LogicalOperations;
|
||||
private static readonly Dictionary<string, Operation> Statements;
|
||||
private static readonly Dictionary<string, Operation> CursorTypes;
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// PropertyFactory creates properties based on template properties (this class public properties)
|
||||
///
|
||||
/// IMPORTANT: Property names should match those in ShowPlanXML classes
|
||||
///
|
||||
/// Note: to hide a property from PropertyGrid, it should be defined
|
||||
/// here with [Browsable(false)] attribute.
|
||||
///
|
||||
/// </summary>
|
||||
internal class PropertyFactory
|
||||
{
|
||||
#region Property templates
|
||||
|
||||
[ShowInToolTip, DisplayOrder(0), DisplayNameDescription(SR.Keys.PhysicalOperation, SR.Keys.PhysicalOperationDesc)]
|
||||
public string PhysicalOp { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(1), DisplayNameDescription(SR.Keys.LogicalOperation, SR.Keys.LogicalOperationDesc)]
|
||||
public string LogicalOp { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.EstimatedExecMode, SR.Keys.EstimatedExecModeDesc)]
|
||||
public string EstimatedExecutionMode { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.ActualExecMode, SR.Keys.ActualExecModeDesc)]
|
||||
public string ActualExecutionMode { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(3), DisplayNameDescription(SR.Keys.Storage, SR.Keys.StorageDesc)]
|
||||
public string Storage { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(102), DisplayNameDescription(SR.Keys.EstimatedDataSize, SR.Keys.EstimatedDataSizeDescription)]
|
||||
[TypeConverter(typeof(DataSizeTypeConverter))]
|
||||
public double EstimatedDataSize { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(4), DisplayNameDescription(SR.Keys.NumberOfRows, SR.Keys.NumberOfRowsDescription)]
|
||||
public double ActualRows { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(4), DisplayNameDescription(SR.Keys.ActualRowsRead, SR.Keys.ActualRowsReadDescription)]
|
||||
public double ActualRowsRead { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(5), DisplayNameDescription(SR.Keys.NumberOfBatches, SR.Keys.NumberOfBatchesDescription)]
|
||||
public double ActualBatches { get { return 0; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(6), DisplayNameDescription(SR.Keys.Statement, SR.Keys.StatementDesc)]
|
||||
public string StatementText { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(6), DisplayNameDescription(SR.Keys.Predicate, SR.Keys.PredicateDescription)]
|
||||
public string Predicate { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(101), DisplayNameDescription(SR.Keys.EstimatedRowSize, SR.Keys.EstimatedRowSizeDescription)]
|
||||
[TypeConverter(typeof(DataSizeTypeConverter))]
|
||||
public int AvgRowSize { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(7), DisplayNameDescription(SR.Keys.CachedPlanSize, SR.Keys.CachedPlanSizeDescription)]
|
||||
[TypeConverter(typeof(KBSizeTypeConverter))]
|
||||
public int CachedPlanSize { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(7), DisplayNameDescription(SR.Keys.UsePlan)]
|
||||
public bool UsePlan { get { return false; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(7), DisplayNameDescription(SR.Keys.ContainsInlineScalarTsqlUdfs)]
|
||||
|
||||
public bool ContainsInlineScalarTsqlUdfs { get { return false; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(8), DisplayNameDescription(SR.Keys.EstimatedIoCost, SR.Keys.EstimatedIoCostDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimateIO { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(8), DisplayNameDescription(SR.Keys.DegreeOfParallelism, SR.Keys.DegreeOfParallelismDescription)]
|
||||
public int DegreeOfParallelism { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(8), DisplayNameDescription(SR.Keys.EffectiveDegreeOfParallelism, SR.Keys.EffectiveDegreeOfParallelismDescription)]
|
||||
public int EffectiveDegreeOfParallelism { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(9), DisplayNameDescription(SR.Keys.EstimatedCpuCost, SR.Keys.EstimatedCpuCostDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimateCPU { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(9), DisplayNameDescription(SR.Keys.MemoryGrant, SR.Keys.MemoryGrantDescription)]
|
||||
[TypeConverter(typeof(KBSizeTypeConverter))]
|
||||
public ulong MemoryGrant { get { return 0; } }
|
||||
|
||||
[DisplayOrder(10), DisplayNameDescription(SR.Keys.ParameterList, SR.Keys.ParameterListDescription)]
|
||||
public object ParameterList { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(10), DisplayNameDescription(SR.Keys.NumberOfExecutions, SR.Keys.NumberOfExecutionsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double ActualExecutions { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(10), DisplayNameDescription(SR.Keys.EstimatedNumberOfExecutions, SR.Keys.EstimatedNumberOfExecutionsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
|
||||
public double EstimateExecutions { get { return 0; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(12), DisplayNameDescription(SR.Keys.ObjectShort, SR.Keys.ObjectDescription)]
|
||||
public object Object { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.IndexKind, SR.Keys.IndexKindDescription)]
|
||||
public string IndexKind { get { return null; } }
|
||||
|
||||
[DisplayOrder(12), DisplayNameDescription(SR.Keys.OperationArgumentShort, SR.Keys.OperationArgumentDescription)]
|
||||
public string Argument { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(111), DisplayNameDescription(SR.Keys.ActualRebinds, SR.Keys.ActualRebindsDescription)]
|
||||
public object ActualRebinds { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(112), DisplayNameDescription(SR.Keys.ActualRewinds, SR.Keys.ActualRewindsDescription)]
|
||||
|
||||
public object ActualRewinds { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLocallyAggregatedRows, SR.Keys.ActualLocallyAggregatedRowsDescription)]
|
||||
public object ActualLocallyAggregatedRows { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualElapsedms, SR.Keys.ActualElapsedmsDescription)]
|
||||
public object ActualElapsedms { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualCPUms, SR.Keys.ActualCPUmsDescription)]
|
||||
public object ActualCPUms { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualScans, SR.Keys.ActualScansDescription)]
|
||||
public object ActualScans { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLogicalReads, SR.Keys.ActualLogicalReadsDescription)]
|
||||
public object ActualLogicalReads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualPhysicalReads, SR.Keys.ActualPhysicalReadsDescription)]
|
||||
public object ActualPhysicalReads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualPageServerReads, SR.Keys.ActualPageServerReadsDescription)]
|
||||
public object ActualPageServerReads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualReadAheads, SR.Keys.ActualReadAheadsDescription)]
|
||||
public object ActualReadAheads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualPageServerReadAheads, SR.Keys.ActualPageServerReadAheadsDescription)]
|
||||
public object ActualPageServerReadAheads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLobLogicalReads, SR.Keys.ActualLobLogicalReadsDescription)]
|
||||
public object ActualLobLogicalReads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLobPhysicalReads, SR.Keys.ActualLobPhysicalReadsDescription)]
|
||||
public object ActualLobPhysicalReads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLobPageServerReads, SR.Keys.ActualLobPageServerReadsDescription)]
|
||||
public object ActualLobPageServerReads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLobReadAheads, SR.Keys.ActualLobReadAheadsDescription)]
|
||||
public object ActualLobReadAheads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualLobPageServerReadAheads, SR.Keys.ActualLobPageServerReadAheadsDescription)]
|
||||
public object ActualLobPageServerReadAheads { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualIOStatistics, SR.Keys.ActualIOStatisticsDescription)]
|
||||
public object ActualIOStatistics { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualTimeStatistics, SR.Keys.ActualTimeStatisticsDescription)]
|
||||
public object ActualTimeStatistics { get { return null; } }
|
||||
|
||||
[DisplayOrder(221), DisplayNameDescription(SR.Keys.ActualMemoryGrantStats, SR.Keys.ActualMemoryGrantStats)]
|
||||
public object ActualMemoryGrantStats { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.HpcRowCount, SR.Keys.HpcRowCountDescription)]
|
||||
public object HpcRowCount { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.HpcKernelElapsedUs, SR.Keys.HpcKernelElapsedUsDescription)]
|
||||
public object HpcKernelElapsedUs { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.HpcHostToDeviceBytes, SR.Keys.HpcHostToDeviceBytesDescription)]
|
||||
public object HpcHostToDeviceBytes { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(221), DisplayNameDescription(SR.Keys.HpcDeviceToHostBytes, SR.Keys.HpcDeviceToHostBytesDescription)]
|
||||
public object HpcDeviceToHostBytes { get { return null; } }
|
||||
|
||||
[DisplayOrder(221), DisplayNameDescription(SR.Keys.InputMemoryGrant, SR.Keys.InputMemoryGrant)]
|
||||
public object InputMemoryGrant { get { return null; } }
|
||||
|
||||
[DisplayOrder(221), DisplayNameDescription(SR.Keys.OutputMemoryGrant, SR.Keys.OutputMemoryGrant)]
|
||||
public object OutputMemoryGrant { get { return null; } }
|
||||
|
||||
[DisplayOrder(221), DisplayNameDescription(SR.Keys.UsedMemoryGrant, SR.Keys.UsedMemoryGrant)]
|
||||
public object UsedMemoryGrant { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.IsGraphDBTransitiveClosure, SR.Keys.IsGraphDBTransitiveClosureDescription)]
|
||||
public bool IsGraphDBTransitiveClosure { get { return false; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.IsInterleavedExecuted, SR.Keys.IsInterleavedExecutedDescription)]
|
||||
public bool IsInterleavedExecuted { get { return false; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.IsAdaptive, SR.Keys.IsAdaptiveDescription)]
|
||||
public bool IsAdaptive { get { return false; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.AdaptiveThresholdRows, SR.Keys.AdaptiveThresholdRowsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double AdaptiveThresholdRows { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.EstimatedJoinType, SR.Keys.EstimatedJoinTypeDescription)]
|
||||
public string EstimatedJoinType { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(2), DisplayNameDescription(SR.Keys.ActualJoinType, SR.Keys.ActualJoinTypeDescription)]
|
||||
public string ActualJoinType { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(100), DisplayNameDescription(SR.Keys.EstimatedNumberOfRowsPerExecution, SR.Keys.EstimatedNumberOfRowsPerExecutionDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimateRows { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(100), DisplayNameDescription(SR.Keys.EstimatedNumberOfRowsPerExecution, SR.Keys.EstimatedNumberOfRowsPerExecutionDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double StatementEstRows { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(100), DisplayNameDescription(SR.Keys.EstimatedNumberOfRowsForAllExecutions, SR.Keys.EstimatedNumberOfRowsForAllExecutionsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimateRowsAllExecs { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(100), DisplayNameDescription(SR.Keys.EstimatedNumberOfRowsForAllExecutions, SR.Keys.EstimatedNumberOfRowsForAllExecutionsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double StatementEstRowsAllExecs { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(100), DisplayNameDescription(SR.Keys.EstimatedRowsRead, SR.Keys.EstimatedRowsReadDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimatedRowsRead { get { return 0; } }
|
||||
|
||||
[DisplayOrder(101), DisplayNameDescription(SR.Keys.EstimatedRebinds, SR.Keys.EstimatedRebindsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimateRebinds { get { return 0; } }
|
||||
|
||||
[DisplayOrder(102), DisplayNameDescription(SR.Keys.EstimatedRewinds, SR.Keys.EstimatedRewindsDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double EstimateRewinds { get { return 0; } }
|
||||
|
||||
[DisplayOrder(200), DisplayNameDescription(SR.Keys.DefinedValues, SR.Keys.DefinedValuesDescription)]
|
||||
public string DefinedValues { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(201), DisplayNameDescription(SR.Keys.OutputList, SR.Keys.OutputListDescription)]
|
||||
public object OutputList { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(202), DisplayNameDescription(SR.Keys.Warnings, SR.Keys.WarningsDescription)]
|
||||
public object Warnings { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.Parallel, SR.Keys.ParallelDescription)]
|
||||
public bool Parallel { get { return false; } }
|
||||
|
||||
[DisplayOrder(204), DisplayNameDescription(SR.Keys.SetOptions, SR.Keys.SetOptionsDescription)]
|
||||
public object StatementSetOptions { get { return null; } }
|
||||
|
||||
[DisplayOrder(205), DisplayNameDescription(SR.Keys.OptimizationLevel, SR.Keys.OptimizationLevelDescription)]
|
||||
public string StatementOptmLevel { get { return null; } }
|
||||
|
||||
[DisplayOrder(206), DisplayNameDescription(SR.Keys.StatementOptmEarlyAbortReason)]
|
||||
public string StatementOptmEarlyAbortReason { get { return null; } }
|
||||
|
||||
[DisplayOrder(211), DisplayNameDescription(SR.Keys.MemoryFractions, SR.Keys.MemoryFractionsDescription)]
|
||||
public object MemoryFractions { get { return null; } }
|
||||
|
||||
[DisplayOrder(211), DisplayNameDescription(SR.Keys.MemoryFractionsInput, SR.Keys.MemoryFractionsInputDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double Input { get { return 0; } }
|
||||
|
||||
[DisplayOrder(212), DisplayNameDescription(SR.Keys.MemoryFractionsOutput, SR.Keys.MemoryFractionsOutputDescription)]
|
||||
[TypeConverter(typeof(FloatTypeConverter))]
|
||||
public double Output { get { return 0; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.RemoteDestination, SR.Keys.RemoteDestinationDescription)]
|
||||
public string RemoteDestination { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.RemoteObject, SR.Keys.RemoteObjectDescription)]
|
||||
public string RemoteObject { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.RemoteSource, SR.Keys.RemoteSourceDescription)]
|
||||
public string RemoteSource { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.RemoteQuery, SR.Keys.RemoteQueryDescription)]
|
||||
public string RemoteQuery { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.UsedUdxColumns, SR.Keys.UsedUdxColumnsDescription)]
|
||||
public object UsedUDXColumns { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(204), DisplayNameDescription(SR.Keys.UdxName, SR.Keys.UdxNameDescription)]
|
||||
public string UDXName { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.InnerSideJoinColumns, SR.Keys.InnerSideJoinColumnsDescription)]
|
||||
public object InnerSideJoinColumns { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(204), DisplayNameDescription(SR.Keys.OuterSideJoinColumns, SR.Keys.OuterSideJoinColumnsDescription)]
|
||||
public object OuterSideJoinColumns { get { return null; } }
|
||||
|
||||
[DisplayOrder(205), DisplayNameDescription(SR.Keys.Residual, SR.Keys.ResidualDescription)]
|
||||
public string Residual { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(206), DisplayNameDescription(SR.Keys.PassThru, SR.Keys.PassThruDescription)]
|
||||
public string PassThru { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(207), DisplayNameDescription(SR.Keys.ManyToMany, SR.Keys.ManyToManyDescription)]
|
||||
public bool ManyToMany { get { return false; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.PartitionColumns, SR.Keys.PartitionColumnsDescription)]
|
||||
public object PartitionColumns { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(204), DisplayNameDescription(SR.Keys.OrderBy, SR.Keys.OrderByDescription)]
|
||||
public object OrderBy { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(205), DisplayNameDescription(SR.Keys.HashKeys, SR.Keys.HashKeysDescription)]
|
||||
public object HashKeys { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(206), DisplayNameDescription(SR.Keys.ProbeColumn, SR.Keys.ProbeColumnDescription)]
|
||||
public object ProbeColumn { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(207), DisplayNameDescription(SR.Keys.PartitioningType, SR.Keys.PartitioningTypeDescription)]
|
||||
public string PartitioningType { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.GroupBy, SR.Keys.GroupByDescription)]
|
||||
public object GroupBy { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.GroupingSets, SR.Keys.GroupingSetsDescription)]
|
||||
public object GroupingSets { get { return null; } }
|
||||
|
||||
[DisplayOrder(200), DisplayNameDescription(SR.Keys.RollupInfo, SR.Keys.RollupInfoDescription)]
|
||||
public object RollupInfo { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.HighestLevel, SR.Keys.HighestLevelDescription)]
|
||||
public object HighestLevel { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.RollupLevel, SR.Keys.RollupLevelDescription)]
|
||||
[Browsable(true), ImmutableObject(true)]
|
||||
public object RollupLevel { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.Level, SR.Keys.LevelDescription)]
|
||||
public object Level { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.SegmentColumn, SR.Keys.SegmentColumnDescription)]
|
||||
public object SegmentColumn { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.HashKeysBuild, SR.Keys.HashKeysBuildDescription)]
|
||||
public object HashKeysBuild { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.HashKeysProbe, SR.Keys.HashKeysProbeDescription)]
|
||||
public object HashKeysProbe { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.BuildResidual, SR.Keys.BuildResidualDescription)]
|
||||
public string BuildResidual { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.ProbeResidual, SR.Keys.ProbeResidualDescription)]
|
||||
public string ProbeResidual { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.SetPredicate, SR.Keys.SetPredicateDescription)]
|
||||
public string SetPredicate { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.RankColumns, SR.Keys.RankColumnsDescription)]
|
||||
public object RankColumns { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.ActionColumn, SR.Keys.ActionColumnDescription)]
|
||||
public object ActionColumn { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.OriginalActionColumn, SR.Keys.OriginalActionColumnDescription)]
|
||||
public object OriginalActionColumn { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.Rows, SR.Keys.RowsDescription)]
|
||||
public int Rows { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(150), DisplayNameDescription(SR.Keys.Partitioned, SR.Keys.PartitionedDescription)]
|
||||
public object Partitioned { get { return null; } }
|
||||
|
||||
[DisplayOrder(156), DisplayNameDescription(SR.Keys.PartitionsAccessed)]
|
||||
public object PartitionsAccessed { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(152), DisplayNameDescription(SR.Keys.PartitionCount)]
|
||||
public object PartitionCount { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.TieColumns, SR.Keys.TieColumnsDescription)]
|
||||
public object TieColumns { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.IsPercent, SR.Keys.IsPercentDescription)]
|
||||
public bool IsPercent { get { return false; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.WithTies, SR.Keys.WithTiesDescription)]
|
||||
public bool WithTies { get { return false; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.TopExpression, SR.Keys.TopExpressionDescription)]
|
||||
public string TopExpression { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.Distinct, SR.Keys.DistinctDescription)]
|
||||
public bool Distinct { get { return false; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(205), DisplayNameDescription(SR.Keys.OuterReferences, SR.Keys.OuterReferencesDescription)]
|
||||
public object OuterReferences { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.PartitionId, SR.Keys.PartitionIdDescription)]
|
||||
public object PartitionId { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.Ordered, SR.Keys.OrderedDescription)]
|
||||
public bool Ordered { get { return false; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.ScanDirection, SR.Keys.ScanDirectionDescription)]
|
||||
public object ScanDirection { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.SeekPredicate, SR.Keys.SeekPredicateDescription)]
|
||||
public object SeekPredicate { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.SeekPredicate, SR.Keys.SeekPredicateDescription)]
|
||||
public object SeekPredicateNew { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(203), DisplayNameDescription(SR.Keys.SeekPredicate, SR.Keys.SeekPredicateDescription)]
|
||||
public object SeekPredicatePart { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(205), DisplayNameDescription(SR.Keys.SeekPredicates, SR.Keys.SeekPredicatesDescription)]
|
||||
public string SeekPredicates { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.ForcedIndex, SR.Keys.ForcedIndexDescription)]
|
||||
public bool ForcedIndex { get { return false; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(5), DisplayNameDescription(SR.Keys.Values, SR.Keys.ValuesDescription)]
|
||||
public object Values { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.ColumnsWithNoStatistics, SR.Keys.ColumnsWithNoStatisticsDescription)]
|
||||
public object ColumnsWithNoStatistics { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.NoJoinPredicate, SR.Keys.NoJoinPredicateDescription)]
|
||||
public bool NoJoinPredicate { get { return false; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.SpillToTempDb, SR.Keys.SpillToTempDbDescription)]
|
||||
public object SpillToTempDb { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.StartupExpression, SR.Keys.StartupExpressionDescription)]
|
||||
public bool StartupExpression { get { return false; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.Query)]
|
||||
public string Query { get { return null; } }
|
||||
|
||||
[DisplayOrder(203), DisplayNameDescription(SR.Keys.Stack)]
|
||||
public bool Stack { get { return false; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(203), DisplayNameDescription(SR.Keys.RowCount)]
|
||||
public bool RowCount { get { return false; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.Optimized)]
|
||||
public bool Optimized { get { return false; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.WithPrefetch)]
|
||||
public bool WithPrefetch { get { return false; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.Prefix)]
|
||||
public object Prefix { get { return null; } }
|
||||
|
||||
[DisplayOrder(7), DisplayNameDescription(SR.Keys.StartRange, SR.Keys.StartRangeDescription)]
|
||||
public object StartRange { get { return null; } }
|
||||
|
||||
[DisplayOrder(8), DisplayNameDescription(SR.Keys.EndRange, SR.Keys.EndRangeDescription)]
|
||||
public object EndRange { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.RangeColumns)]
|
||||
public object RangeColumns { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.RangeExpressions)]
|
||||
public object RangeExpressions { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ScanType)]
|
||||
public object ScanType { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ColumnReference)]
|
||||
public object ColumnReference { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectServer, SR.Keys.ObjectServerDescription)]
|
||||
public string Server { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectDatabase, SR.Keys.ObjectDatabaseDescription)]
|
||||
public string Database { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectIndex, SR.Keys.ObjectIndexDescription)]
|
||||
public string Index { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectSchema, SR.Keys.ObjectSchemaDescription)]
|
||||
public string Schema { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectTable, SR.Keys.ObjectTableDescription)]
|
||||
public string Table { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectAlias, SR.Keys.ObjectAliasDescription)]
|
||||
public string Alias { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectColumn, SR.Keys.ObjectColumnDescription)]
|
||||
public string Column { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ObjectComputedColumn, SR.Keys.ObjectComputedColumnDescription)]
|
||||
public bool ComputedColumn { get { return false; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ParameterDataType)]
|
||||
public string ParameterDataType { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ParameterCompiledValue)]
|
||||
public string ParameterCompiledValue { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ParameterRuntimeValue)]
|
||||
public string ParameterRuntimeValue { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.CursorPlan)]
|
||||
public object CursorPlan { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.CursorOperation)]
|
||||
public object Operation { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.CursorName)]
|
||||
public string CursorName { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.CursorActualType)]
|
||||
public object CursorActualType { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.CursorRequestedType)]
|
||||
public object CursorRequestedType { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.CursorConcurrency)]
|
||||
public object CursorConcurrency { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.ForwardOnly)]
|
||||
public bool ForwardOnly { get { return false; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.QueryPlan)]
|
||||
public object QueryPlan { get { return null; } }
|
||||
|
||||
[DisplayOrder(6), DisplayNameDescription(SR.Keys.OperationType)]
|
||||
public object OperationType { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(300), DisplayNameDescription(SR.Keys.NodeId)]
|
||||
public int NodeId { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(301), DisplayNameDescription(SR.Keys.PrimaryNodeId)]
|
||||
public int PrimaryNodeId { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(302), DisplayNameDescription(SR.Keys.ForeignKeyReferencesCount)]
|
||||
public int ForeignKeyReferencesCount { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(303), DisplayNameDescription(SR.Keys.NoMatchingIndexCount)]
|
||||
public int NoMatchingIndexCount { get { return 0; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(304), DisplayNameDescription(SR.Keys.PartialMatchingIndexCount)]
|
||||
public int PartialMatchingIndexCount { get { return 0; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(6), DisplayNameDescription(SR.Keys.WhereJoinColumns)]
|
||||
public object WhereJoinColumns { get { return null; } }
|
||||
|
||||
[ShowInToolTip(LongString = true), DisplayOrder(6), DisplayNameDescription(SR.Keys.ProcName)]
|
||||
public string ProcName { get { return null; } }
|
||||
|
||||
[DisplayOrder(400), DisplayNameDescription(SR.Keys.InternalInfo)]
|
||||
public object InternalInfo { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(220), DisplayNameDescription(SR.Keys.RemoteDataAccess, SR.Keys.RemoteDataAccessDescription)]
|
||||
public bool RemoteDataAccess { get { return false; } }
|
||||
|
||||
[DisplayOrder(220), DisplayNameDescription(SR.Keys.CloneAccessScope, SR.Keys.CloneAccessScopeDescription)]
|
||||
public string CloneAccessScope { get { return null; } }
|
||||
|
||||
[ShowInToolTip, DisplayOrder(220), DisplayNameDescription(SR.Keys.Remoting, SR.Keys.RemotingDescription)]
|
||||
public bool Remoting { get { return false; } }
|
||||
|
||||
[DisplayOrder(201), DisplayNameDescription(SR.Keys.Activation)]
|
||||
public object Activation { get { return null; } }
|
||||
|
||||
[DisplayOrder(201), DisplayNameDescription(SR.Keys.BrickRouting)]
|
||||
public object BrickRouting { get { return null; } }
|
||||
|
||||
[DisplayOrder(201), DisplayNameDescription(SR.Keys.FragmentIdColumn)]
|
||||
public object FragmentIdColumn { get { return null; } }
|
||||
public string CardinalityEstimationModelVersion { get { return null; } }
|
||||
public string CompileCPU { get { return null; } }
|
||||
public string CompileMemory { get { return null; } }
|
||||
public string CompileTime { get { return null; } }
|
||||
public string NonParallelPlanReason { get { return null; } }
|
||||
public string QueryHash { get { return null; } }
|
||||
public string QueryPlanHash { get { return null; } }
|
||||
public bool RetrievedFromCache { get { return false; } }
|
||||
public bool SecurityPolicyApplied { get { return false; } }
|
||||
public bool NoExpandHint { get { return false; } }
|
||||
public double TableCardinality { get { return 0; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Non-browsable properties
|
||||
|
||||
// The following properties should be hidden from UI
|
||||
|
||||
|
||||
[Browsable(false)]
|
||||
public string PhysicalOperationKind { get { return null; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public double EstimatedTotalSubtreeCost { get { return 0; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public double StatementSubTreeCost { get { return 0; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public double TotalSubtreeCost { get { return 0; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public int Parent { get { return 0; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public int StatementId { get { return 0; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public int StatementCompId { get { return 0; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public object RunTimeInformation { get { return null; } }
|
||||
|
||||
[Browsable(false)]
|
||||
public object StatementType { get { return null; } }
|
||||
|
||||
/// <summary>
|
||||
/// Run time partition summary should not show up as one node. Details such as PartitionsAccessed is displayed in individually.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public object RunTimePartitionSummary { get { return null; } }
|
||||
[Browsable(false)]
|
||||
public object SkeletonNode { get { return null; } }
|
||||
[Browsable(false)]
|
||||
public object SkeletonHasMatch { get { return null; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region CreateProperty
|
||||
public static PropertyDescriptor CreateProperty(PropertyDescriptor property, object value)
|
||||
{
|
||||
Type type = null;
|
||||
|
||||
// In case of xml Choice group, the property name can be general like "Item" or "Items".
|
||||
// The real names are specified by XmlElementAttributes. We need to save the type of
|
||||
// value to extract its original name from its XmlElementAttribute.
|
||||
if (property.Name == "Items" || property.Name == "Item")
|
||||
{
|
||||
type = value.GetType();
|
||||
}
|
||||
|
||||
// Convert value if ObjectWrapperTypeConverter supports it
|
||||
if (ObjectWrapperTypeConverter.Default.CanConvertFrom(property.PropertyType))
|
||||
{
|
||||
value = ObjectWrapperTypeConverter.Default.ConvertFrom(value);
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
PropertyDescriptor templateProperty = Properties[property.Name];
|
||||
|
||||
if (templateProperty != null)
|
||||
{
|
||||
return new PropertyValue(templateProperty, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable attributeCollection = property.Attributes;
|
||||
string propertyName = property.Name;
|
||||
|
||||
// In case of xml Choice group, the property name can be general like "Item" or "Items".
|
||||
// The real names are specified by XmlElementAttributes. property.Attributes does not
|
||||
// return all the attributes. Hence we need extract custom attributes through
|
||||
// PropertyInfo class.
|
||||
if (type != null)
|
||||
{
|
||||
attributeCollection = PropertyFactory.GetAttributeCollectionForChoiceElement(property);
|
||||
}
|
||||
|
||||
foreach (object attrib in attributeCollection)
|
||||
{
|
||||
XmlElementAttribute attribute = attrib as XmlElementAttribute;
|
||||
|
||||
if (attribute != null && !string.IsNullOrEmpty(attribute.ElementName))
|
||||
{
|
||||
if ((type == null) || (type.Equals(attribute.Type)))
|
||||
{
|
||||
propertyName = attribute.ElementName;
|
||||
|
||||
templateProperty = Properties[propertyName];
|
||||
if (templateProperty != null)
|
||||
{
|
||||
return new PropertyValue(templateProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: review this debug code
|
||||
return new PropertyValue(propertyName, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable GetAttributeCollectionForChoiceElement(PropertyDescriptor property)
|
||||
{
|
||||
Type type = property.ComponentType;
|
||||
PropertyInfo pInfo = type.GetProperty("Items");
|
||||
|
||||
if (pInfo == null)
|
||||
{
|
||||
//Try using item.
|
||||
pInfo = type.GetProperty("Item");
|
||||
}
|
||||
|
||||
if (pInfo != null)
|
||||
{
|
||||
return pInfo.GetCustomAttributes(true);
|
||||
}
|
||||
|
||||
return property.Attributes;
|
||||
}
|
||||
public static PropertyDescriptor CreateProperty(string propertyName, object value)
|
||||
{
|
||||
PropertyDescriptor templateProperty = Properties[propertyName];
|
||||
|
||||
if (templateProperty != null)
|
||||
{
|
||||
return new PropertyValue(templateProperty, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: review this debug code
|
||||
return new PropertyValue(propertyName, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation details
|
||||
|
||||
private PropertyFactory() { }
|
||||
|
||||
private static PropertyDescriptorCollection Properties = TypeDescriptor.GetProperties(typeof(PropertyFactory));
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
internal sealed class PropertyValue : PropertyDescriptor
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
public PropertyValue(string name, object value)
|
||||
: base(name, null)
|
||||
{
|
||||
this.propertyValue = value;
|
||||
}
|
||||
|
||||
public PropertyValue(PropertyDescriptor baseProperty, object value)
|
||||
: this(baseProperty.Name, value)
|
||||
{
|
||||
this.baseProperty = baseProperty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods and properties
|
||||
|
||||
public object Value
|
||||
{
|
||||
get { return this.propertyValue; }
|
||||
set { this.propertyValue = value; }
|
||||
}
|
||||
|
||||
public int DisplayOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeDisplayAttributesIfNecessary();
|
||||
return this.displayOrder;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLongString
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeDisplayAttributesIfNecessary();
|
||||
return this.isLongString;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDisplayNameAndDescription(string newDisplayName, string newDescription)
|
||||
{
|
||||
this.displayName = newDisplayName;
|
||||
this.description = newDescription;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PropertyDesciptor overrides
|
||||
|
||||
public override AttributeCollection Attributes
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.baseProperty != null ? baseProperty.Attributes : base.Attributes;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanResetValue(object component)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override Type ComponentType
|
||||
{
|
||||
get { return this.GetType(); }
|
||||
}
|
||||
|
||||
public override Type PropertyType
|
||||
{
|
||||
get { return this.propertyValue != null ? this.propertyValue.GetType() : typeof(string); }
|
||||
}
|
||||
|
||||
public override object GetValue(object component)
|
||||
{
|
||||
return this.propertyValue;
|
||||
}
|
||||
|
||||
public override void ResetValue(object component)
|
||||
{
|
||||
}
|
||||
|
||||
public override void SetValue(object component, object value)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool ShouldSerializeValue(object component)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeDisplayAttributesIfNecessary();
|
||||
|
||||
if (this.displayName != null || this.displayNameKey != null)
|
||||
{
|
||||
if (this.displayName == null)
|
||||
{
|
||||
this.displayName = SR.Keys.GetString(this.displayNameKey);
|
||||
}
|
||||
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
return base.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeDisplayAttributesIfNecessary();
|
||||
|
||||
if (this.description != null || this.descriptionKey != null)
|
||||
{
|
||||
if (this.description == null)
|
||||
{
|
||||
this.description = SR.Keys.GetString(this.descriptionKey);
|
||||
}
|
||||
|
||||
return this.description;
|
||||
}
|
||||
|
||||
return base.Description;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void InitializeDisplayAttributesIfNecessary()
|
||||
{
|
||||
if (this.initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
DisplayNameDescriptionAttribute displayNameDescriptionAttribute =
|
||||
Attributes[typeof(DisplayNameDescriptionAttribute)] as DisplayNameDescriptionAttribute;
|
||||
if (displayNameDescriptionAttribute != null)
|
||||
{
|
||||
this.displayNameKey = displayNameDescriptionAttribute.DisplayName;
|
||||
this.descriptionKey = displayNameDescriptionAttribute.Description;
|
||||
if (this.descriptionKey == null)
|
||||
{
|
||||
this.descriptionKey = this.displayNameKey;
|
||||
}
|
||||
}
|
||||
|
||||
DisplayOrderAttribute displayOrderAttribute =
|
||||
Attributes[typeof(DisplayOrderAttribute)] as DisplayOrderAttribute;
|
||||
if (displayOrderAttribute != null)
|
||||
{
|
||||
this.displayOrder = displayOrderAttribute.DisplayOrder;
|
||||
}
|
||||
|
||||
ShowInToolTipAttribute showInToolTipAttribute =
|
||||
Attributes[typeof(ShowInToolTipAttribute)] as ShowInToolTipAttribute;
|
||||
if (showInToolTipAttribute != null)
|
||||
{
|
||||
this.isLongString = showInToolTipAttribute.LongString;
|
||||
}
|
||||
}
|
||||
|
||||
#region Private members
|
||||
|
||||
private object propertyValue;
|
||||
private string displayName;
|
||||
private string displayNameKey;
|
||||
private string description;
|
||||
private string descriptionKey;
|
||||
private int displayOrder = Int32.MaxValue;
|
||||
private PropertyDescriptor baseProperty;
|
||||
private bool isLongString;
|
||||
private bool initialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region OrderComparer
|
||||
|
||||
internal sealed class OrderComparer : IComparer
|
||||
{
|
||||
int IComparer.Compare(object x, object y)
|
||||
{
|
||||
return Compare(x as PropertyValue, y as PropertyValue);
|
||||
}
|
||||
|
||||
private static int Compare(PropertyValue x, PropertyValue y)
|
||||
{
|
||||
if (x.IsLongString != y.IsLongString)
|
||||
{
|
||||
return x.IsLongString ? 1 : -1;
|
||||
}
|
||||
|
||||
int orderOfX = x != null ? x.DisplayOrder : Int32.MaxValue - 1;
|
||||
int orderOfY = y != null ? y.DisplayOrder : Int32.MaxValue - 1;
|
||||
|
||||
return orderOfX - orderOfY;
|
||||
}
|
||||
|
||||
public static readonly OrderComparer Default = new OrderComparer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses ShowPlan XML objects derived from RelOpBaseType type
|
||||
/// </summary>
|
||||
internal class RelOpBaseTypeParser : XmlPlanParser
|
||||
{
|
||||
/// <summary>
|
||||
/// This function doesn't do anything. It simply returns the parent node
|
||||
/// passed it.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentItem">Parent item.</param>
|
||||
/// <param name="parentNode">Parent node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
/// <returns>The node that corresponds to the item being parsed.</returns>
|
||||
public override Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates children items of the item being parsed.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public override IEnumerable GetChildren(object parsedItem)
|
||||
{
|
||||
PropertyDescriptor relOpProperty = TypeDescriptor.GetProperties(parsedItem)["RelOp"];
|
||||
if (relOpProperty != null)
|
||||
{
|
||||
object value = relOpProperty.GetValue(parsedItem);
|
||||
if (value != null)
|
||||
{
|
||||
if (value is IEnumerable)
|
||||
{
|
||||
foreach (object item in (IEnumerable)value)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
protected RelOpBaseTypeParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static RelOpBaseTypeParser relOpBaseTypeParser = null;
|
||||
public static RelOpBaseTypeParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (relOpBaseTypeParser == null)
|
||||
{
|
||||
relOpBaseTypeParser = new RelOpBaseTypeParser();
|
||||
}
|
||||
return relOpBaseTypeParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,733 @@
|
||||
//
|
||||
// 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.Text;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
internal sealed class RelOpTypeParser : XmlPlanParser
|
||||
{
|
||||
#region Constants
|
||||
private const string OPERATION_INDEX_DELETE = "IndexDelete";
|
||||
private const string OPERATION_CLUSTERED_INDEX_DELETE = "ClusteredIndexDelete";
|
||||
private const string OPERATION_COLUMNSTORE_INDEX_DELETE = "ColumnstoreIndexDelete";
|
||||
|
||||
private const string OPERATION_INDEX_INSERT = "IndexInsert";
|
||||
private const string OPERATION_CLUSTERED_INDEX_INSERT = "ClusteredIndexInsert";
|
||||
private const string OPERATION_COLUMNSTORE_INDEX_INSERT = "ColumnstoreIndexInsert";
|
||||
|
||||
private const string OPERATION_INDEX_MERGE = "IndexMerge";
|
||||
private const string OPERATION_CLUSTERED_INDEX_MERGE = "ClusteredIndexMerge";
|
||||
private const string OPERATION_COLUMNSTORE_INDEX_MERGE = "ColumnstoreIndexMerge";
|
||||
|
||||
private const string OPERATION_INDEX_SCAN = "IndexScan";
|
||||
private const string OPERATION_CLUSTERED_INDEX_SCAN = "ClusteredIndexScan";
|
||||
private const string OPERATION_COLUMNSTORE_INDEX_SCAN = "ColumnstoreIndexScan";
|
||||
|
||||
private const string OPERATION_INDEX_UPDATE = "IndexUpdate";
|
||||
private const string OPERATION_CLUSTERED_INDEX_UPDATE = "ClusteredIndexUpdate";
|
||||
private const string OPERATION_COLUMNSTORE_INDEX_UPDATE = "ColumnstoreIndexUpdate";
|
||||
|
||||
private const string OBJECT_NODE = "Object";
|
||||
private const string STORAGE_PROPERTY = "Storage";
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates new node and adds it to the graph.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentItem">Parent item.</param>
|
||||
/// <param name="parentNode">Parent node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
/// <returns>The node that corresponds to the item being parsed.</returns>
|
||||
public override Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
return NewNode(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates children items of the item being parsed.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public override IEnumerable GetChildren(object parsedItem)
|
||||
{
|
||||
RelOpType item = parsedItem as RelOpType;
|
||||
if (item.Item != null)
|
||||
{
|
||||
yield return item.Item;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines Operation that corresponds to the object being parsed.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Operation that corresponds to the node.</returns>
|
||||
protected override Operation GetNodeOperation(Node node)
|
||||
{
|
||||
object physicalOpType = node["PhysicalOp"];
|
||||
object logicalOpType = node["LogicalOp"];
|
||||
|
||||
if (physicalOpType == null || logicalOpType == null)
|
||||
{
|
||||
throw new FormatException(SR.Keys.UnknownShowPlanSource);
|
||||
}
|
||||
|
||||
string physicalOpTypeName = physicalOpType.ToString();
|
||||
string logicalOpTypeName = logicalOpType.ToString();
|
||||
|
||||
// SQLBU# 434739: Custom description and icons for KeyLookup operation:
|
||||
//
|
||||
// SQL Server 2005 doesnt expose 'KeyLookup' operations as thier own type,
|
||||
// instead they indicate Bookmark operations as a 'ClusteredIndexSeek' op
|
||||
// that is having Lookup=true. Users have to select the actual node to tell
|
||||
// if a ClusteredIndexSeek is an actual bookmark operation or not.
|
||||
//
|
||||
// Our request for having engine expose the Bookmark operation as its own type,
|
||||
// instead of exposing it as a 'ClusteredIndexSeek' cannot be addressed by
|
||||
// engine in SP2 timeframe (reasons include compatibility on published showplanxml.xsd
|
||||
// schema as well as amount of changes in components that consume the xml showplan)
|
||||
//
|
||||
// For SP2 timeframe the solution is to do an aesthetic only change:
|
||||
// SSMS interprets the xml showplan and provides custom icons and descriptions
|
||||
// for a new operation: 'KeyLookup', that is getting documented in BOL.
|
||||
const string operationClusteredIndexSeek = "ClusteredIndexSeek";
|
||||
const string operationKeyLookup = "KeyLookup";
|
||||
|
||||
object lookup = node["Lookup"];
|
||||
if ((lookup != null) && (lookup is System.Boolean))
|
||||
{
|
||||
if (Convert.ToBoolean(lookup) == true)
|
||||
{
|
||||
if (0 == string.Compare(physicalOpTypeName, operationClusteredIndexSeek, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
physicalOpTypeName = operationKeyLookup;
|
||||
}
|
||||
if (0 == string.Compare(logicalOpTypeName, operationClusteredIndexSeek, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logicalOpTypeName = operationKeyLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For index scans, Storage property should be read from this node.
|
||||
* Otherwise, for DML operations, Storage property should be read from this node's child "Object" element.
|
||||
*/
|
||||
if (0 == string.Compare(physicalOpTypeName, OPERATION_INDEX_SCAN, StringComparison.OrdinalIgnoreCase) ||
|
||||
0 == string.Compare(physicalOpTypeName, OPERATION_CLUSTERED_INDEX_SCAN, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
object storage = node[STORAGE_PROPERTY];
|
||||
if ((storage != null) && (storage.Equals(StorageType.ColumnStore)))
|
||||
{
|
||||
physicalOpTypeName = OPERATION_COLUMNSTORE_INDEX_SCAN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpandableObjectWrapper objectWrapper = (ExpandableObjectWrapper)node[OBJECT_NODE];
|
||||
if (objectWrapper != null)
|
||||
{
|
||||
PropertyValue storagePropertyValue = (PropertyValue)objectWrapper.Properties[STORAGE_PROPERTY];
|
||||
|
||||
/*
|
||||
* If object's storage is of type Storage.Columnstore,
|
||||
* PhysicalOperations should be updated to their columnstore counterparts.
|
||||
*/
|
||||
if (storagePropertyValue != null && ((storagePropertyValue).Value.Equals(StorageType.ColumnStore)))
|
||||
{
|
||||
if (0 == string.Compare(physicalOpTypeName, OPERATION_INDEX_DELETE, StringComparison.OrdinalIgnoreCase) ||
|
||||
0 == string.Compare(physicalOpTypeName, OPERATION_CLUSTERED_INDEX_DELETE, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
physicalOpTypeName = OPERATION_COLUMNSTORE_INDEX_DELETE;
|
||||
}
|
||||
else if (0 == string.Compare(physicalOpTypeName, OPERATION_INDEX_INSERT, StringComparison.OrdinalIgnoreCase) ||
|
||||
0 == string.Compare(physicalOpTypeName, OPERATION_CLUSTERED_INDEX_INSERT, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
physicalOpTypeName = OPERATION_COLUMNSTORE_INDEX_INSERT;
|
||||
}
|
||||
else if (0 == string.Compare(physicalOpTypeName, OPERATION_INDEX_MERGE, StringComparison.OrdinalIgnoreCase) ||
|
||||
0 == string.Compare(physicalOpTypeName, OPERATION_CLUSTERED_INDEX_MERGE, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
physicalOpTypeName = OPERATION_COLUMNSTORE_INDEX_MERGE;
|
||||
}
|
||||
else if (0 == string.Compare(physicalOpTypeName, OPERATION_INDEX_UPDATE, StringComparison.OrdinalIgnoreCase) ||
|
||||
0 == string.Compare(physicalOpTypeName, OPERATION_CLUSTERED_INDEX_UPDATE, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
physicalOpTypeName = OPERATION_COLUMNSTORE_INDEX_UPDATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Operation physicalOp = OperationTable.GetPhysicalOperation(physicalOpTypeName);
|
||||
Operation logicalOp = OperationTable.GetLogicalOperation(logicalOpTypeName);
|
||||
|
||||
Operation resultOp = logicalOp != null && logicalOp.Image != null && logicalOp.Description != null
|
||||
? logicalOp : physicalOp;
|
||||
|
||||
node.LogicalOpUnlocName = logicalOpTypeName;
|
||||
node.PhysicalOpUnlocName = physicalOpTypeName;
|
||||
node["PhysicalOp"] = physicalOp.DisplayName;
|
||||
node["LogicalOp"] = logicalOp.DisplayName;
|
||||
|
||||
Debug.Assert(logicalOp.DisplayName != null);
|
||||
Debug.Assert(physicalOp.DisplayName != null);
|
||||
Debug.Assert(resultOp.Description != null);
|
||||
Debug.Assert(resultOp.Image != null);
|
||||
|
||||
return resultOp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines node subtree cost from existing node properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Node subtree cost.</returns>
|
||||
protected override double GetNodeSubtreeCost(Node node)
|
||||
{
|
||||
object value = node["PDWAccumulativeCost"] ?? node["EstimatedTotalSubtreeCost"];
|
||||
return value != null ? Convert.ToDouble(value, CultureInfo.CurrentCulture) : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates node special properties such as Operator, Cost, SubtreeCost.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
public override void ParseProperties(object parsedItem, PropertyDescriptorCollection targetPropertyBag, NodeBuilderContext context)
|
||||
{
|
||||
base.ParseProperties(parsedItem, targetPropertyBag, context);
|
||||
|
||||
RelOpType item = parsedItem as RelOpType;
|
||||
Debug.Assert(item != null);
|
||||
|
||||
if (item.RunTimeInformation != null && item.RunTimeInformation.Length > 0)
|
||||
{
|
||||
RunTimeCounters actualRowCountCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualRowsReadCountCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualBatchCountCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualRebindsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualRewindsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualExecutionsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLocallyAggregatedRowsCountCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualElapsedTimeCounter = new RunTimeCounters { DisplayTotalCounters = false };
|
||||
RunTimeCounters actualElapsedCPUTimeCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualScansCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLogicalReadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualPhysicalReadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualPageServerReadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualReadAheadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualPageServerReadAheadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLobLogicalReadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLobPhysicalReadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLobPageServerReadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLobReadAheadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualLobPageServerReadAheadsCounter = new RunTimeCounters();
|
||||
RunTimeCounters actualInputMemoryGrantCounter = new MemGrantRunTimeCounters();
|
||||
RunTimeCounters actualOutputMemoryGrantCounter = new MemGrantRunTimeCounters();
|
||||
RunTimeCounters actualUsedMemoryGrantCounter = new RunTimeCounters();
|
||||
|
||||
RunTimeCounters hpcKernelElapsedUsCounter = new RunTimeCounters();
|
||||
RunTimeCounters hpcRowCountCounter = new RunTimeCounters();
|
||||
RunTimeCounters hpcHostToDeviceBytesCounter = new RunTimeCounters();
|
||||
RunTimeCounters hpcDeviceToHostBytesCounter = new RunTimeCounters();
|
||||
|
||||
ExpandableObjectWrapper actualTimeStatsObjWrapper = new ExpandableObjectWrapper();
|
||||
ExpandableObjectWrapper actualIOStatsObjWrapper = new ExpandableObjectWrapper();
|
||||
ExpandableObjectWrapper actualMemoryGrantStatsObjWrapper = new ExpandableObjectWrapper();
|
||||
|
||||
String actualExecutionModeValue = String.Empty;
|
||||
String actualJoinTypeValue = String.Empty;
|
||||
bool actualIsInterleavedExecuted = false;
|
||||
|
||||
foreach (RunTimeInformationTypeRunTimeCountersPerThread counter in item.RunTimeInformation)
|
||||
{
|
||||
if (counter.BrickIdSpecified)
|
||||
{
|
||||
actualRowCountCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualRows);
|
||||
actualRebindsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualRebinds);
|
||||
actualRewindsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualRewinds);
|
||||
actualExecutionsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualExecutions);
|
||||
actualLocallyAggregatedRowsCountCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLocallyAggregatedRows);
|
||||
|
||||
if (counter.ActualElapsedmsSpecified)
|
||||
{
|
||||
actualElapsedTimeCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualElapsedms);
|
||||
}
|
||||
|
||||
if (counter.ActualCPUmsSpecified)
|
||||
{
|
||||
actualElapsedCPUTimeCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualCPUms);
|
||||
}
|
||||
|
||||
if (counter.ActualScansSpecified)
|
||||
{
|
||||
actualScansCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualScans);
|
||||
}
|
||||
|
||||
if (counter.ActualLogicalReadsSpecified)
|
||||
{
|
||||
actualLogicalReadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLogicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualPhysicalReadsSpecified)
|
||||
{
|
||||
actualPhysicalReadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualPhysicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualPageServerReadsSpecified)
|
||||
{
|
||||
actualPageServerReadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualPageServerReads);
|
||||
}
|
||||
|
||||
if (counter.ActualReadAheadsSpecified)
|
||||
{
|
||||
actualReadAheadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualPageServerReadAheadsSpecified)
|
||||
{
|
||||
actualPageServerReadAheadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualPageServerReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobLogicalReadsSpecified)
|
||||
{
|
||||
actualLobLogicalReadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLobLogicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobPhysicalReadsSpecified)
|
||||
{
|
||||
actualLobPhysicalReadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLobPhysicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobPageServerReadsSpecified)
|
||||
{
|
||||
actualLobPageServerReadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLobPageServerReads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobReadAheadsSpecified)
|
||||
{
|
||||
actualLobReadAheadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLobReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobPageServerReadAheadsSpecified)
|
||||
{
|
||||
actualLobPageServerReadAheadsCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualLobPageServerReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualRowsReadSpecified)
|
||||
{
|
||||
actualRowsReadCountCounter.AddCounter(counter.Thread, counter.BrickId, counter.ActualRowsRead);
|
||||
}
|
||||
|
||||
if (counter.BatchesSpecified)
|
||||
{
|
||||
actualBatchCountCounter.AddCounter(counter.Thread, counter.BrickId, counter.Batches);
|
||||
}
|
||||
|
||||
if (counter.HpcRowCountSpecified)
|
||||
{
|
||||
hpcRowCountCounter.AddCounter(counter.Thread, counter.BrickId, counter.HpcRowCount);
|
||||
}
|
||||
|
||||
if (counter.HpcKernelElapsedUsSpecified)
|
||||
{
|
||||
hpcKernelElapsedUsCounter.AddCounter(counter.Thread, counter.BrickId, counter.HpcKernelElapsedUs);
|
||||
}
|
||||
|
||||
if (counter.HpcHostToDeviceBytesSpecified)
|
||||
{
|
||||
hpcHostToDeviceBytesCounter.AddCounter(counter.Thread, counter.BrickId, counter.HpcHostToDeviceBytes);
|
||||
}
|
||||
|
||||
if (counter.HpcDeviceToHostBytesSpecified)
|
||||
{
|
||||
hpcDeviceToHostBytesCounter.AddCounter(counter.Thread, counter.BrickId, counter.HpcDeviceToHostBytes);
|
||||
}
|
||||
|
||||
if (counter.InputMemoryGrantSpecified)
|
||||
{
|
||||
actualInputMemoryGrantCounter.AddCounter(counter.Thread, counter.BrickId, counter.InputMemoryGrant);
|
||||
}
|
||||
|
||||
if (counter.OutputMemoryGrantSpecified)
|
||||
{
|
||||
actualOutputMemoryGrantCounter.AddCounter(counter.Thread, counter.BrickId, counter.OutputMemoryGrant);
|
||||
}
|
||||
|
||||
if (counter.UsedMemoryGrantSpecified)
|
||||
{
|
||||
actualUsedMemoryGrantCounter.AddCounter(counter.Thread, counter.BrickId, counter.UsedMemoryGrant);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
actualRowCountCounter.AddCounter(counter.Thread, counter.ActualRows);
|
||||
actualRebindsCounter.AddCounter(counter.Thread, counter.ActualRebinds);
|
||||
actualRewindsCounter.AddCounter(counter.Thread, counter.ActualRewinds);
|
||||
actualExecutionsCounter.AddCounter(counter.Thread, counter.ActualExecutions);
|
||||
actualLocallyAggregatedRowsCountCounter.AddCounter(counter.Thread, counter.ActualLocallyAggregatedRows);
|
||||
|
||||
if (counter.ActualElapsedmsSpecified)
|
||||
{
|
||||
actualElapsedTimeCounter.AddCounter(counter.Thread, counter.ActualElapsedms);
|
||||
}
|
||||
|
||||
if (counter.ActualCPUmsSpecified)
|
||||
{
|
||||
actualElapsedCPUTimeCounter.AddCounter(counter.Thread, counter.ActualCPUms);
|
||||
}
|
||||
|
||||
if (counter.ActualScansSpecified)
|
||||
{
|
||||
actualScansCounter.AddCounter(counter.Thread, counter.ActualScans);
|
||||
}
|
||||
|
||||
if (counter.ActualLogicalReadsSpecified)
|
||||
{
|
||||
actualLogicalReadsCounter.AddCounter(counter.Thread, counter.ActualLogicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualPhysicalReadsSpecified)
|
||||
{
|
||||
actualPhysicalReadsCounter.AddCounter(counter.Thread, counter.ActualPhysicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualPageServerReadsSpecified)
|
||||
{
|
||||
actualPageServerReadsCounter.AddCounter(counter.Thread, counter.ActualPageServerReads);
|
||||
}
|
||||
|
||||
if (counter.ActualReadAheadsSpecified)
|
||||
{
|
||||
actualReadAheadsCounter.AddCounter(counter.Thread, counter.ActualReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualPageServerReadAheadsSpecified)
|
||||
{
|
||||
actualPageServerReadAheadsCounter.AddCounter(counter.Thread, counter.ActualPageServerReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobLogicalReadsSpecified)
|
||||
{
|
||||
actualLobLogicalReadsCounter.AddCounter(counter.Thread, counter.ActualLobLogicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobPhysicalReadsSpecified)
|
||||
{
|
||||
actualLobPhysicalReadsCounter.AddCounter(counter.Thread, counter.ActualLobPhysicalReads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobPageServerReadsSpecified)
|
||||
{
|
||||
actualLobPageServerReadsCounter.AddCounter(counter.Thread, counter.ActualLobPageServerReads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobReadAheadsSpecified)
|
||||
{
|
||||
actualLobReadAheadsCounter.AddCounter(counter.Thread, counter.ActualLobReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualLobPageServerReadAheadsSpecified)
|
||||
{
|
||||
actualLobPageServerReadAheadsCounter.AddCounter(counter.Thread, counter.ActualLobPageServerReadAheads);
|
||||
}
|
||||
|
||||
if (counter.ActualRowsReadSpecified)
|
||||
{
|
||||
actualRowsReadCountCounter.AddCounter(counter.Thread, counter.ActualRowsRead);
|
||||
}
|
||||
|
||||
if (counter.BatchesSpecified)
|
||||
{
|
||||
actualBatchCountCounter.AddCounter(counter.Thread, counter.Batches);
|
||||
}
|
||||
|
||||
if (counter.HpcRowCountSpecified)
|
||||
{
|
||||
hpcRowCountCounter.AddCounter(counter.Thread, counter.HpcRowCount);
|
||||
}
|
||||
|
||||
if (counter.HpcKernelElapsedUsSpecified)
|
||||
{
|
||||
hpcKernelElapsedUsCounter.AddCounter(counter.Thread, counter.HpcKernelElapsedUs);
|
||||
}
|
||||
|
||||
if (counter.HpcHostToDeviceBytesSpecified)
|
||||
{
|
||||
hpcHostToDeviceBytesCounter.AddCounter(counter.Thread, counter.HpcHostToDeviceBytes);
|
||||
}
|
||||
|
||||
if (counter.HpcDeviceToHostBytesSpecified)
|
||||
{
|
||||
hpcDeviceToHostBytesCounter.AddCounter(counter.Thread, counter.HpcDeviceToHostBytes);
|
||||
}
|
||||
|
||||
if (counter.InputMemoryGrantSpecified)
|
||||
{
|
||||
actualInputMemoryGrantCounter.AddCounter(counter.Thread, counter.InputMemoryGrant);
|
||||
}
|
||||
|
||||
if (counter.OutputMemoryGrantSpecified)
|
||||
{
|
||||
actualOutputMemoryGrantCounter.AddCounter(counter.Thread, counter.OutputMemoryGrant);
|
||||
}
|
||||
|
||||
if (counter.UsedMemoryGrantSpecified)
|
||||
{
|
||||
actualUsedMemoryGrantCounter.AddCounter(counter.Thread, counter.UsedMemoryGrant);
|
||||
}
|
||||
}
|
||||
|
||||
if (counter.ActualExecutions > 0)
|
||||
{
|
||||
actualExecutionModeValue = Enum.GetName(typeof(ExecutionModeType), counter.ActualExecutionMode);
|
||||
}
|
||||
|
||||
if (counter.ActualJoinTypeSpecified)
|
||||
{
|
||||
actualJoinTypeValue = Enum.GetName(typeof(PhysicalOpType), counter.ActualJoinType);
|
||||
}
|
||||
|
||||
if (counter.IsInterleavedExecuted)
|
||||
{
|
||||
actualIsInterleavedExecuted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualIsInterleavedExecuted)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("IsInterleavedExecuted", actualIsInterleavedExecuted));
|
||||
}
|
||||
|
||||
// Create localizable properties and add them to the property bag
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualRows", actualRowCountCounter));
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualBatches", actualBatchCountCounter));
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualRebinds", actualRebindsCounter));
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualRewinds", actualRewindsCounter));
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualExecutions", actualExecutionsCounter));
|
||||
|
||||
if (actualRowsReadCountCounter.TotalCounters > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualRowsRead", actualRowsReadCountCounter));
|
||||
}
|
||||
|
||||
if (actualLocallyAggregatedRowsCountCounter.TotalCounters > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualLocallyAggregatedRows", actualLocallyAggregatedRowsCountCounter));
|
||||
}
|
||||
|
||||
if (hpcRowCountCounter.TotalCounters > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("HpcRowCount", hpcRowCountCounter));
|
||||
}
|
||||
|
||||
if (hpcKernelElapsedUsCounter.TotalCounters > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("HpcKernelElapsedUs", hpcKernelElapsedUsCounter));
|
||||
}
|
||||
|
||||
if (hpcHostToDeviceBytesCounter.TotalCounters > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("HpcHostToDeviceBytes", hpcHostToDeviceBytesCounter));
|
||||
}
|
||||
|
||||
if (hpcDeviceToHostBytesCounter.TotalCounters > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("HpcDeviceToHostBytes", hpcDeviceToHostBytesCounter));
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(actualExecutionModeValue))
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualExecutionMode", actualExecutionModeValue));
|
||||
}
|
||||
|
||||
|
||||
if (!String.IsNullOrEmpty(actualJoinTypeValue))
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualJoinType", actualJoinTypeValue));
|
||||
}
|
||||
|
||||
// Populate the "Actual Time Statistics" property if applicable
|
||||
// Nested properties include "Actual Elapsed Time" and "Actual Elapsed CPU Time"
|
||||
if (actualElapsedTimeCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualTimeStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualElapsedms", actualElapsedTimeCounter));
|
||||
}
|
||||
|
||||
if (actualElapsedCPUTimeCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualTimeStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualCPUms", actualElapsedCPUTimeCounter));
|
||||
}
|
||||
|
||||
if (actualTimeStatsObjWrapper.Properties.Count > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualTimeStatistics", actualTimeStatsObjWrapper));
|
||||
}
|
||||
|
||||
// Populate the "Actual IO Statistics" property if applicable
|
||||
// Nested properties include "Scan" and "Read" properties.
|
||||
if (actualScansCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualScans", actualScansCounter));
|
||||
}
|
||||
|
||||
if (actualLogicalReadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualLogicalReads", actualLogicalReadsCounter));
|
||||
}
|
||||
|
||||
if (actualPhysicalReadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualPhysicalReads", actualPhysicalReadsCounter));
|
||||
}
|
||||
|
||||
if (actualPageServerReadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualPageServerReads", actualPageServerReadsCounter));
|
||||
}
|
||||
|
||||
if (actualReadAheadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualReadAheads", actualReadAheadsCounter));
|
||||
}
|
||||
|
||||
if (actualPageServerReadAheadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualPageServerReadAheads", actualPageServerReadAheadsCounter));
|
||||
}
|
||||
|
||||
if (actualLobLogicalReadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualLobLogicalReads", actualLobLogicalReadsCounter));
|
||||
}
|
||||
|
||||
if (actualLobPhysicalReadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualLobPhysicalReads", actualLobPhysicalReadsCounter));
|
||||
}
|
||||
|
||||
if (actualLobPageServerReadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualLobPageServerReads", actualLobPageServerReadsCounter));
|
||||
}
|
||||
|
||||
if (actualLobReadAheadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualLobReadAheads", actualLobReadAheadsCounter));
|
||||
}
|
||||
|
||||
if (actualLobPageServerReadAheadsCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualIOStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("ActualLobPageServerReadAheads", actualLobPageServerReadAheadsCounter));
|
||||
}
|
||||
|
||||
if (actualIOStatsObjWrapper.Properties.Count > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualIOStatistics", actualIOStatsObjWrapper));
|
||||
}
|
||||
|
||||
// Populate ActualMemoryGrantStats
|
||||
if (actualInputMemoryGrantCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualMemoryGrantStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("InputMemoryGrant", actualInputMemoryGrantCounter));
|
||||
}
|
||||
|
||||
if (actualOutputMemoryGrantCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualMemoryGrantStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("OutputMemoryGrant", actualOutputMemoryGrantCounter));
|
||||
}
|
||||
|
||||
if (actualUsedMemoryGrantCounter.NumOfCounters > 0)
|
||||
{
|
||||
actualMemoryGrantStatsObjWrapper.Properties.Add(PropertyFactory.CreateProperty("UsedMemoryGrant", actualUsedMemoryGrantCounter));
|
||||
}
|
||||
|
||||
if (actualMemoryGrantStatsObjWrapper.Properties.Count > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("ActualMemoryGrantStats", actualMemoryGrantStatsObjWrapper));
|
||||
}
|
||||
}
|
||||
|
||||
// Decompose RunTimePartitionSummary and add them individually.
|
||||
// Otherwise, the properties will show up as nested in the property window.
|
||||
if (item.RunTimePartitionSummary != null && item.RunTimePartitionSummary.PartitionsAccessed != null)
|
||||
{
|
||||
RunTimePartitionSummaryTypePartitionsAccessed partitions = item.RunTimePartitionSummary.PartitionsAccessed;
|
||||
|
||||
// Create localizable properties and add them to the property bag
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("PartitionCount", partitions.PartitionCount));
|
||||
|
||||
if (partitions.PartitionRange != null && partitions.PartitionRange.Length > 0)
|
||||
{
|
||||
targetPropertyBag.Add(PropertyFactory.CreateProperty("PartitionsAccessed", GetPartitionRangeString(partitions.PartitionRange)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to format partition range string.
|
||||
/// </summary>
|
||||
/// <param name="ranges">Partition ranges</param>
|
||||
/// <returns>property string</returns>
|
||||
private static string GetPartitionRangeString(RunTimePartitionSummaryTypePartitionsAccessedPartitionRange[] ranges)
|
||||
{
|
||||
Debug.Assert(ranges != null);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
string separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
stringBuilder.Append(separator);
|
||||
}
|
||||
|
||||
RunTimePartitionSummaryTypePartitionsAccessedPartitionRange range = ranges[i];
|
||||
if (range.Start == range.End)
|
||||
{
|
||||
// The range is a single number
|
||||
stringBuilder.Append(range.Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.AppendFormat(CultureInfo.CurrentCulture, "{0}..{1}", range.Start, range.End);
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
private RelOpTypeParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static RelOpTypeParser relOpTypeParser = null;
|
||||
public static RelOpTypeParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (relOpTypeParser == null)
|
||||
{
|
||||
relOpTypeParser = new RelOpTypeParser();
|
||||
}
|
||||
return relOpTypeParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
//
|
||||
// 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;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// RunTimeCounters class stores RunTimeCountersPerThread information
|
||||
/// </summary>
|
||||
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
|
||||
internal class RunTimeCounters : ICustomTypeDescriptor
|
||||
{
|
||||
#region Inner classes
|
||||
|
||||
protected struct Counter
|
||||
{
|
||||
public int Thread;
|
||||
public int BrickId;
|
||||
public bool BrickIdSpecified;
|
||||
public ulong Value;
|
||||
|
||||
public Counter(int thread, ulong value)
|
||||
{
|
||||
Thread = thread;
|
||||
BrickIdSpecified = false;
|
||||
BrickId = 0;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public Counter(int thread, int brickId, ulong value)
|
||||
{
|
||||
Thread = thread;
|
||||
BrickIdSpecified = true;
|
||||
BrickId = brickId;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
ulong totalCounters;
|
||||
ulong maxCounter;
|
||||
protected List<Counter> counters = new List<Counter>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RunTimeCounters()
|
||||
{
|
||||
maxCounter = 0;
|
||||
DisplayTotalCounters = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods and properties
|
||||
|
||||
public void AddCounter(int thread, ulong counterValue)
|
||||
{
|
||||
this.counters.Add(new Counter(thread, counterValue));
|
||||
this.totalCounters += counterValue;
|
||||
if (counterValue > maxCounter)
|
||||
{
|
||||
maxCounter = counterValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCounter(int thread, int brickId, ulong counterValue)
|
||||
{
|
||||
this.counters.Add(new Counter(thread, brickId, counterValue));
|
||||
this.totalCounters += counterValue;
|
||||
if (counterValue > maxCounter)
|
||||
{
|
||||
maxCounter = counterValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sum of values passed to AddCounter
|
||||
/// </summary>
|
||||
public ulong TotalCounters
|
||||
{
|
||||
get { return this.totalCounters; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// max value passed to AddCounter
|
||||
/// </summary>
|
||||
public ulong MaxCounter
|
||||
{
|
||||
get { return this.maxCounter; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// if true, display TotalCounters as string representation, otherwise display MaxCounter
|
||||
/// </summary>
|
||||
public bool DisplayTotalCounters
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of Counter objects added to counters list
|
||||
/// Does not represent the calculated total count.
|
||||
/// </summary>
|
||||
public int NumOfCounters
|
||||
{
|
||||
get { return this.counters.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// string representation of RunTimeCounters
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
// display max counter value as the string representation of this class for specific properties, for ex ActualElapsedms
|
||||
// for other properties, display total counter value
|
||||
if (DisplayTotalCounters)
|
||||
{
|
||||
return TotalCounters.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MaxCounter.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICustomTypeDescriptor
|
||||
|
||||
AttributeCollection ICustomTypeDescriptor.GetAttributes()
|
||||
{
|
||||
return TypeDescriptor.GetAttributes(GetType());
|
||||
}
|
||||
|
||||
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
|
||||
{
|
||||
return TypeDescriptor.GetDefaultEvent(GetType());
|
||||
}
|
||||
|
||||
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
|
||||
{
|
||||
return TypeDescriptor.GetDefaultProperty(GetType());
|
||||
}
|
||||
|
||||
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
|
||||
{
|
||||
return TypeDescriptor.GetEditor(GetType(), editorBaseType);
|
||||
}
|
||||
|
||||
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
|
||||
{
|
||||
return TypeDescriptor.GetEvents(GetType());
|
||||
}
|
||||
|
||||
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
|
||||
{
|
||||
return TypeDescriptor.GetEvents(GetType(), attributes );
|
||||
}
|
||||
|
||||
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor propertyDescriptor)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
|
||||
{
|
||||
PropertyDescriptor[] propertiesDescriptors = new PropertyDescriptor[this.counters.Count];
|
||||
string description = SR.Keys.PerThreadCounterDescription;
|
||||
|
||||
if (this.counters.Count == 1)
|
||||
{
|
||||
PropertyValue property;
|
||||
if (this.counters[0].BrickIdSpecified)
|
||||
{
|
||||
property = new PropertyValue(SR.RuntimeCounterThreadOnInstance(this.counters[0].Thread, this.counters[0].BrickId), this.counters[0].Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
property = new PropertyValue(SR.RuntimeCounterThreadAll, this.counters[0].Value);
|
||||
}
|
||||
property.SetDisplayNameAndDescription(property.Name, description);
|
||||
propertiesDescriptors[0] = property;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<this.counters.Count; i++)
|
||||
{
|
||||
PropertyValue property;
|
||||
if (this.counters[i].BrickIdSpecified)
|
||||
{
|
||||
property = new PropertyValue(SR.RuntimeCounterThreadOnInstance(this.counters[i].Thread, this.counters[i].BrickId), this.counters[i].Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
property = new PropertyValue(SR.RuntimeCounterThread(this.counters[i].Thread), this.counters[i].Value);
|
||||
}
|
||||
property.SetDisplayNameAndDescription(property.Name, description);
|
||||
propertiesDescriptors[i] = property;
|
||||
}
|
||||
}
|
||||
|
||||
return new PropertyDescriptorCollection(propertiesDescriptors);
|
||||
}
|
||||
|
||||
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
|
||||
{
|
||||
return ((ICustomTypeDescriptor)this).GetProperties();
|
||||
}
|
||||
|
||||
string ICustomTypeDescriptor.GetComponentName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeConverter ICustomTypeDescriptor.GetConverter()
|
||||
{
|
||||
return TypeDescriptor.GetConverter(GetType());
|
||||
}
|
||||
|
||||
string ICustomTypeDescriptor.GetClassName()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// derived class that overrides ToString for memory grant related properties
|
||||
/// </summary>
|
||||
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
|
||||
internal class MemGrantRunTimeCounters : RunTimeCounters
|
||||
{
|
||||
/// <summary>
|
||||
/// string representation of MemGrantRunTimeCounters
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
ulong displayValue = this.TotalCounters;
|
||||
|
||||
// if there is more than one thread/counter, memory grant from thread 0 is not used so it doesn't carry meaningful counter value and needs to ignored
|
||||
if (this.NumOfCounters > 1)
|
||||
{
|
||||
// find thread 0 counter value, note it may not be the first element in counters list
|
||||
foreach (var ct in this.counters)
|
||||
{
|
||||
if (ct.Thread == 0)
|
||||
{
|
||||
displayValue -= ct.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return displayValue.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses stytement type ShowPlan XML nodes
|
||||
/// </summary>
|
||||
internal class StatementParser : XmlPlanParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates new node and adds it to the graph.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentItem">Parent item.</param>
|
||||
/// <param name="parentNode">Parent node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
/// <returns>The node that corresponds to the item being parsed.</returns>
|
||||
public override Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
return NewNode(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines Operation that corresponds to the object being parsed.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Operation that corresponds to the node.</returns>
|
||||
protected override Operation GetNodeOperation(Node node)
|
||||
{
|
||||
object statementType = node["StatementType"];
|
||||
Operation statement = statementType != null
|
||||
? OperationTable.GetStatement(statementType.ToString())
|
||||
: Operation.Unknown;
|
||||
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines node subtree cost from existing node properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Node subtree cost.</returns>
|
||||
protected override double GetNodeSubtreeCost(Node node)
|
||||
{
|
||||
object value = node["StatementSubTreeCost"];
|
||||
return value != null ? Convert.ToDouble(value, CultureInfo.CurrentCulture) : 0;
|
||||
}
|
||||
|
||||
protected override bool ShouldParseItem(object parsedItem)
|
||||
{
|
||||
// Special case. An empty statement without QueryPlan but with
|
||||
// a UDF or StoreProc should be skipped
|
||||
StmtSimpleType statement = parsedItem as StmtSimpleType;
|
||||
if (statement != null)
|
||||
{
|
||||
// We use hidden wrapper statements for UDFs and StoredProcs
|
||||
// Hidden statements don't have any of their properties defined
|
||||
// We can use one of properties which is always set by server
|
||||
// such as StatementIdSpecified to distinguish between a real
|
||||
// statement and a hidden wrapper statement
|
||||
if (!statement.StatementIdSpecified)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// By default, the statement is parsed
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates FunctionType blocks and removes all items from UDF and StoredProc properties.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public override IEnumerable<FunctionTypeItem> ExtractFunctions(object parsedItem)
|
||||
{
|
||||
StmtSimpleType statement = parsedItem as StmtSimpleType;
|
||||
if (statement != null)
|
||||
{
|
||||
// If this is a simple statement it may have UDF and StoredProc fields
|
||||
if (statement.UDF != null)
|
||||
{
|
||||
foreach (FunctionType function in statement.UDF)
|
||||
{
|
||||
yield return new FunctionTypeItem(function, FunctionTypeItem.ItemType.Udf);
|
||||
}
|
||||
statement.UDF = null;
|
||||
}
|
||||
|
||||
if (statement.StoredProc != null)
|
||||
{
|
||||
yield return new FunctionTypeItem(statement.StoredProc, FunctionTypeItem.ItemType.StoredProcedure);
|
||||
statement.StoredProc = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is some other type of Statement. Call ExtractFunctions for all its children
|
||||
foreach (object item in GetChildren(parsedItem))
|
||||
{
|
||||
XmlPlanParser parser = XmlPlanParserFactory.GetParser(item.GetType());
|
||||
foreach (FunctionTypeItem functionItem in parser.ExtractFunctions(item))
|
||||
{
|
||||
yield return functionItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// protected constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
protected StatementParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static StatementParser statementParser = null;
|
||||
public static StatementParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (statementParser == null)
|
||||
{
|
||||
statementParser = new StatementParser();
|
||||
}
|
||||
return statementParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
internal class XmlPlanHierarchyParser : XmlPlanParser
|
||||
{
|
||||
/// <summary>
|
||||
/// This function doesn't do anything. It simply returns the parent node
|
||||
/// passed it.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentItem">Parent item.</param>
|
||||
/// <param name="parentNode">Parent node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
/// <returns>The node that corresponds to the item being parsed.</returns>
|
||||
public override Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts FunctionType blocks.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public override IEnumerable<FunctionTypeItem> ExtractFunctions(object parsedItem)
|
||||
{
|
||||
// Recursively call ExtractFunctions for each children.
|
||||
foreach (object item in GetChildren(parsedItem))
|
||||
{
|
||||
XmlPlanParser parser = XmlPlanParserFactory.GetParser(item.GetType());
|
||||
foreach (FunctionTypeItem functionItem in parser.ExtractFunctions(item))
|
||||
{
|
||||
yield return functionItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor prevents this object from being externally instantiated
|
||||
/// </summary>
|
||||
protected XmlPlanHierarchyParser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singelton instance
|
||||
/// </summary>
|
||||
private static XmlPlanHierarchyParser xmlPlanHierarchyParser = null;
|
||||
public static XmlPlanHierarchyParser Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (xmlPlanHierarchyParser == null)
|
||||
{
|
||||
xmlPlanHierarchyParser = new XmlPlanHierarchyParser();
|
||||
}
|
||||
return xmlPlanHierarchyParser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
//
|
||||
// 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 System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds hierarchy of Graph objects from ShowPlan XML
|
||||
/// </summary>
|
||||
internal sealed class XmlPlanNodeBuilder : INodeBuilder, IXmlBatchParser
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
public XmlPlanNodeBuilder(ShowPlanType showPlanType)
|
||||
{
|
||||
this.showPlanType = showPlanType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INodeBuilder
|
||||
|
||||
/// <summary>
|
||||
/// Builds one or more Graphs that
|
||||
/// represnet data from the data source.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data Source.</param>
|
||||
/// <returns>An array of AnalysisServices Graph objects.</returns>
|
||||
public ShowPlanGraph[] Execute(object dataSource)
|
||||
{
|
||||
ShowPlanXML plan = dataSource as ShowPlanXML;
|
||||
if (plan == null)
|
||||
{
|
||||
plan = ReadXmlShowPlan(dataSource);
|
||||
}
|
||||
|
||||
List<ShowPlanGraph> graphs = new List<ShowPlanGraph>();
|
||||
|
||||
foreach (BaseStmtInfoType statement in EnumStatements(plan))
|
||||
{
|
||||
// Reset currentNodeId (used through Context) and create new context
|
||||
this.currentNodeId = 0;
|
||||
NodeBuilderContext context = new NodeBuilderContext(new ShowPlanGraph(), this.showPlanType, this);
|
||||
// Parse the statement block
|
||||
XmlPlanParser.Parse(statement, null, null, context);
|
||||
// Add graph to the list
|
||||
graphs.Add(context.Graph);
|
||||
}
|
||||
|
||||
return graphs.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IXmlBatchParser
|
||||
|
||||
/// <summary>
|
||||
/// Returns an XML string for a specific ShowPlan statement.
|
||||
/// This is used to save a plan corresponding to a particular graph control.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data source that contains the full plan.</param>
|
||||
/// <param name="statementIndex">Statement index.</param>
|
||||
/// <returns>XML string that contains execution plan for the specified statement index.</returns>
|
||||
public string GetSingleStatementXml(object dataSource, int statementIndex)
|
||||
{
|
||||
StmtBlockType newStatementBlock = GetSingleStatementObject(dataSource, statementIndex);
|
||||
|
||||
// Now make the new plan based on the existing one that contains only one statement.
|
||||
ShowPlanXML plan = ReadXmlShowPlan(dataSource);
|
||||
plan.BatchSequence = new StmtBlockType[][]
|
||||
{
|
||||
new StmtBlockType[] { newStatementBlock }
|
||||
};
|
||||
|
||||
// Serialize the new plan.
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Serializer.Serialize(new StringWriter(stringBuilder), plan);
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns single statement block type object
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data source</param>
|
||||
/// <param name="statementIndex">Statement index in the data source</param>
|
||||
/// <returns>Single statement block type object</returns>
|
||||
public StmtBlockType GetSingleStatementObject(object dataSource, int statementIndex)
|
||||
{
|
||||
// First read the whole plan from the data source
|
||||
ShowPlanXML plan = ReadXmlShowPlan(dataSource);
|
||||
|
||||
int index = 0;
|
||||
StmtBlockType newStatementBlock = new StmtBlockType();
|
||||
|
||||
// Locate the statement for the specified index
|
||||
foreach (BaseStmtInfoType statement in EnumStatements(plan))
|
||||
{
|
||||
if (statementIndex == index++)
|
||||
{
|
||||
// This is the statement we are looking for
|
||||
newStatementBlock.Items = new BaseStmtInfoType[] { statement };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newStatementBlock.Items == null)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("statementIndex");
|
||||
}
|
||||
|
||||
return newStatementBlock;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets current node Id and internally increments the Id.
|
||||
/// </summary>
|
||||
/// <returns>ID.</returns>
|
||||
internal int GetCurrentNodeId()
|
||||
{
|
||||
return ++currentNodeId;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Implementation details
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes XML ShowPlan from the data source
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data Source</param>
|
||||
/// <returns>ShowPlanXML object which is the root of deserialized plan.</returns>
|
||||
private ShowPlanXML ReadXmlShowPlan(object dataSource)
|
||||
{
|
||||
ShowPlanXML result = null;
|
||||
|
||||
string stringData = dataSource as string;
|
||||
if (stringData != null)
|
||||
{
|
||||
using (StringReader reader = new StringReader(stringData))
|
||||
{
|
||||
result = Serializer.Deserialize(reader) as ShowPlanXML;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] binaryData = dataSource as byte[];
|
||||
if (binaryData != null)
|
||||
{
|
||||
using (MemoryStream stream = new MemoryStream(binaryData))
|
||||
{
|
||||
// We need to use reflection to obtain private method of XmlReader class
|
||||
// that can create a binary reader. Public XmlReader.Create does not
|
||||
// support this.
|
||||
MethodInfo createSqlReaderMethodInfo = typeof(System.Xml.XmlReader).GetMethod("CreateSqlReader", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
object[] args = new object[3] { stream, null, null };
|
||||
|
||||
using (XmlReader reader = (XmlReader)createSqlReaderMethodInfo.Invoke(null, args))
|
||||
{
|
||||
result = Serializer.Deserialize(reader) as ShowPlanXML;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null == result)
|
||||
{
|
||||
Debug.Assert(false, "Unexpected ShowPlan source = " + dataSource.GetType().ToString());
|
||||
throw new ArgumentException(SR.Keys.UnknownShowPlanSource);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates statements in XML ShowPlan. This also looks inside each statement and
|
||||
/// enumerates sub-statements found in FunctionType blocks.
|
||||
/// </summary>
|
||||
/// <param name="plan">XML ShowPlan.</param>
|
||||
/// <returns>Statements enumerator.</returns>
|
||||
private IEnumerable<BaseStmtInfoType> EnumStatements(ShowPlanXML plan)
|
||||
{
|
||||
foreach (StmtBlockType[] statementBatch in plan.BatchSequence)
|
||||
{
|
||||
foreach (StmtBlockType statementBlock in statementBatch)
|
||||
{
|
||||
ExtractFunctions(statementBlock);
|
||||
|
||||
// flatten out any statements contained within then / else clauses to make it appear as though all code paths are
|
||||
// executed sequentially, this is useful for the Live show plan case because it only displays a single show-plan instance at any given time.
|
||||
if (showPlanType == ShowPlanType.Live)
|
||||
{
|
||||
FlattenConditionClauses(statementBlock);
|
||||
}
|
||||
|
||||
foreach (BaseStmtInfoType statement in EnumStatements(statementBlock))
|
||||
{
|
||||
yield return statement;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We do some special handling of the showplan graphs to flatten out control nodes. See VSTS 3657984.
|
||||
/// Essentially the problem is that the Actual showplan and the predicted show plan are treated differently.
|
||||
/// The predicted show plan shows the control node (while, if-then-else) when the actual show plans only contain a single
|
||||
/// plan per statement. This difference makes it difficult to match up the running query against the predicted showplan. Further
|
||||
/// complicating the situation is that each statement may re-use nodeIDs which violates a fundamental assumption
|
||||
/// of the LQS tool and the progress estimators. We can work-around this by flattening out the predicted show plan graph
|
||||
/// to look as a series of statements without the control structures or nesting
|
||||
/// </summary>
|
||||
private void FlattenConditionClauses(StmtBlockType statementBlock)
|
||||
{
|
||||
if (statementBlock != null && statementBlock.Items != null)
|
||||
{
|
||||
ArrayList targetStatementList = new ArrayList();
|
||||
|
||||
foreach (BaseStmtInfoType statement in statementBlock.Items)
|
||||
{
|
||||
targetStatementList.Add(statement);
|
||||
|
||||
FlattenConditionClauses(statement, targetStatementList);
|
||||
}
|
||||
|
||||
// Make a new Items array for the statement block by combining existing items and
|
||||
// new wrapper statements
|
||||
statementBlock.Items = new BaseStmtInfoType[targetStatementList.Count];
|
||||
targetStatementList.CopyTo(statementBlock.Items);
|
||||
}
|
||||
}
|
||||
|
||||
private void FlattenConditionClauses(BaseStmtInfoType statement, ArrayList targetStatementList)
|
||||
{
|
||||
// Enum statement children and genetate wrapper statements for them
|
||||
XmlPlanParser parser = XmlPlanParserFactory.GetParser(statement.GetType());
|
||||
foreach (object child in parser.GetChildren(statement))
|
||||
{
|
||||
StmtCondTypeThen stmtThen = child as StmtCondTypeThen;
|
||||
if (stmtThen != null)
|
||||
{
|
||||
//add this element and its children
|
||||
if (stmtThen.Statements != null && stmtThen.Statements.Items != null)
|
||||
{
|
||||
foreach (BaseStmtInfoType subStatement in stmtThen.Statements.Items)
|
||||
{
|
||||
targetStatementList.Add(subStatement);
|
||||
FlattenConditionClauses(subStatement, targetStatementList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
StmtCondTypeElse stmtElse = child as StmtCondTypeElse;
|
||||
if (stmtElse != null)
|
||||
{
|
||||
//add this element and its children
|
||||
if (stmtElse.Statements != null && stmtElse.Statements.Items != null)
|
||||
{
|
||||
foreach (BaseStmtInfoType subStatement in stmtElse.Statements.Items)
|
||||
{
|
||||
targetStatementList.Add(subStatement);
|
||||
FlattenConditionClauses(subStatement, targetStatementList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts UDF and StoredProc items and places each of them at the top level
|
||||
/// wrapping each of them with an empty statement.
|
||||
/// </summary>
|
||||
/// <param name="statementBlock">Statement block</param>
|
||||
private void ExtractFunctions(StmtBlockType statementBlock)
|
||||
{
|
||||
if (statementBlock != null && statementBlock.Items != null)
|
||||
{
|
||||
ArrayList targetStatementList = new ArrayList();
|
||||
|
||||
foreach (BaseStmtInfoType statement in statementBlock.Items)
|
||||
{
|
||||
targetStatementList.Add(statement);
|
||||
|
||||
ExtractFunctions(statement, targetStatementList);
|
||||
}
|
||||
|
||||
// Make a new Items array for the statement block by combining existing items and
|
||||
// new wrapper statements
|
||||
statementBlock.Items = new BaseStmtInfoType[targetStatementList.Count];
|
||||
targetStatementList.CopyTo(statementBlock.Items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts UDF and StoredProc items from a statement and adds them to a target list.
|
||||
/// </summary>
|
||||
/// <param name="statement">Statement.</param>
|
||||
/// <param name="targetStatementList">Target list to add a newly generated statement to.</param>
|
||||
private void ExtractFunctions(BaseStmtInfoType statement, ArrayList targetStatementList)
|
||||
{
|
||||
// Enum FunctionType objects and generate wrapper statements for them
|
||||
XmlPlanParser parser = XmlPlanParserFactory.GetParser(statement.GetType());
|
||||
foreach (FunctionTypeItem functionItem in parser.ExtractFunctions(statement))
|
||||
{
|
||||
StmtSimpleType subStatement = null;
|
||||
|
||||
if (functionItem.Type == FunctionTypeItem.ItemType.StoredProcedure)
|
||||
{
|
||||
subStatement = new StmtSimpleType();
|
||||
subStatement.StoredProc = functionItem.Function;
|
||||
}
|
||||
else if (functionItem.Type == FunctionTypeItem.ItemType.Udf)
|
||||
{
|
||||
subStatement = new StmtSimpleType();
|
||||
subStatement.UDF = new FunctionType[] { functionItem.Function };
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Ivalid function type");
|
||||
}
|
||||
|
||||
if (subStatement != null)
|
||||
{
|
||||
targetStatementList.Add(subStatement);
|
||||
|
||||
// Call itself recursively.
|
||||
if (functionItem.Function.Statements != null && functionItem.Function.Statements.Items != null)
|
||||
{
|
||||
foreach (BaseStmtInfoType functionStatement in functionItem.Function.Statements.Items)
|
||||
{
|
||||
ExtractFunctions(functionStatement, targetStatementList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively enumerates statements in StmtBlockType.
|
||||
/// </summary>
|
||||
/// <param name="statementBlock">Statement block (may contain multiple statements).</param>
|
||||
/// <returns>Statement enumerator.</returns>
|
||||
private IEnumerable<BaseStmtInfoType> EnumStatements(StmtBlockType statementBlock)
|
||||
{
|
||||
if (statementBlock != null && statementBlock.Items != null)
|
||||
{
|
||||
foreach (BaseStmtInfoType statement in statementBlock.Items)
|
||||
{
|
||||
yield return statement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private members
|
||||
|
||||
private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(ShowPlanXML));
|
||||
|
||||
private ShowPlanType showPlanType;
|
||||
private int currentNodeId;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
//
|
||||
// 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 System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for enumerating FunctionType objects
|
||||
/// </summary>
|
||||
internal sealed class FunctionTypeItem
|
||||
{
|
||||
internal enum ItemType
|
||||
{
|
||||
Unknown,
|
||||
Udf,
|
||||
StoredProcedure
|
||||
};
|
||||
|
||||
internal FunctionTypeItem(FunctionType function, ItemType type)
|
||||
{
|
||||
this.function = function;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
internal FunctionType Function
|
||||
{
|
||||
get { return this.function; }
|
||||
}
|
||||
|
||||
internal ItemType Type
|
||||
{
|
||||
get { return this.type; }
|
||||
}
|
||||
|
||||
private FunctionType function;
|
||||
private ItemType type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all Xml Execution plan node parsers.
|
||||
/// </summary>
|
||||
internal abstract class XmlPlanParser : ObjectParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a ShowPlan item and either creates a new Node or adds properties to
|
||||
/// the provided Node.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being parsed.</param>
|
||||
/// <param name="parentNode">Existing node which is used as a property host or a parent for the new node.</param>
|
||||
/// <param name="context">Node builder context.</param>
|
||||
public static void Parse(object item, object parentItem, Node parentNode, NodeBuilderContext context)
|
||||
{
|
||||
XmlPlanParser parser = XmlPlanParserFactory.GetParser(item.GetType());
|
||||
|
||||
if (parser != null)
|
||||
{
|
||||
Node node = null;
|
||||
|
||||
if (parser.ShouldParseItem(item))
|
||||
{
|
||||
node = parser.GetCurrentNode(item, parentItem, parentNode, context);
|
||||
if (node != null)
|
||||
{
|
||||
// add node/statement mapping to the ShowPlanGraph
|
||||
if (context != null && context.Graph != null && !context.Graph.NodeStmtMap.ContainsKey(node))
|
||||
{
|
||||
context.Graph.NodeStmtMap.Add(node, item);
|
||||
}
|
||||
parser.ParseProperties(item, node.Properties, context);
|
||||
}
|
||||
if(parentNode == null)
|
||||
{
|
||||
context.Graph.Root = node;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node = parentNode;
|
||||
}
|
||||
|
||||
foreach (object child in parser.GetChildren(item))
|
||||
{
|
||||
XmlPlanParser.Parse(child, item, node, context);
|
||||
}
|
||||
|
||||
if (node != parentNode)
|
||||
{
|
||||
parser.SetNodeSpecialProperties(node);
|
||||
if (parentNode != null)
|
||||
{
|
||||
parentNode.AddChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Unexpected run type = " + item.ToString());
|
||||
// Debug.LogExThrow(); {{removed from ssms}}
|
||||
throw new InvalidOperationException(SR.Keys.UnexpectedRunType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="parentItem"></param>
|
||||
/// <param name="parentNode"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public abstract Node GetCurrentNode(object item, object parentItem, Node parentNode, NodeBuilderContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates children items of the item being parsed.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public virtual IEnumerable GetChildren(object parsedItem)
|
||||
{
|
||||
return EnumerateChildren(parsedItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts FunctionType blocks.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">The item being parsed.</param>
|
||||
/// <returns>Enumeration.</returns>
|
||||
public virtual IEnumerable<FunctionTypeItem> ExtractFunctions(object parsedItem)
|
||||
{
|
||||
// By default - no functions
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this node should be parsed
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">ShowPlan item</param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool ShouldParseItem(object parsedItem)
|
||||
{
|
||||
// All items are parsed by default
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates node special properties such as Operator, Cost, SubtreeCost.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
protected virtual void SetNodeSpecialProperties(Node node)
|
||||
{
|
||||
if (node.Operation == null)
|
||||
{
|
||||
node.Operation = GetNodeOperation(node);
|
||||
}
|
||||
|
||||
// Retrieve Subtree cost for this node
|
||||
node.SubtreeCost = GetNodeSubtreeCost(node);
|
||||
|
||||
// EstimateExecutions = EstimateRebinds + EstimateRewinds + 1
|
||||
if (node["EstimateRebinds"] != null && node["EstimateRewinds"] != null)
|
||||
{
|
||||
double estimateRebinds = (double) node["EstimateRebinds"];
|
||||
double estimateRewinds = (double) node["EstimateRewinds"];
|
||||
node["EstimateExecutions"] = estimateRebinds + estimateRewinds + 1;
|
||||
}
|
||||
|
||||
// EstimateRowsAllExecs = EstimateRows * EstimateExecutions
|
||||
double estimateRows = node["EstimateRows"] == null ? 0.0 : Convert.ToDouble(node["EstimateRows"]);
|
||||
double estimateExecutions = node["EstimateExecutions"] == null ? 0.0 : Convert.ToDouble(node["EstimateExecutions"]);
|
||||
double actualExecutions = node["ActualExecutions"] == null ? 0.0 : ((RunTimeCounters)node["ActualExecutions"]).TotalCounters;
|
||||
|
||||
//It's unlikely the total number of rows would exceed DBL_MAX = 1.8*(10^308), thus safe to not check overflow.
|
||||
node["EstimateRowsAllExecs"] = estimateRows * estimateExecutions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the current property is used to reference a child item.
|
||||
/// Hierarchy properties are skipped when property wrappers are being created.
|
||||
/// </summary>
|
||||
/// <param name="property">Property subject to test.</param>
|
||||
/// <returns>True if the property is a hierarchy property;
|
||||
/// false if this is a regular property that should appear in the property grid.
|
||||
/// </returns>
|
||||
protected override bool ShouldSkipProperty(PropertyDescriptor property)
|
||||
{
|
||||
Type type = property.PropertyType;
|
||||
|
||||
if (type.IsArray)
|
||||
{
|
||||
type = type.GetElementType();
|
||||
}
|
||||
|
||||
return XmlPlanParserFactory.GetParser(type) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines Operation that corresponds to the object being parsed.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Operation that corresponds to the node.</returns>
|
||||
protected virtual Operation GetNodeOperation(Node node)
|
||||
{
|
||||
// STrace.Assert(false, "GetNodeOperation should not be called on base class."); {{aasim useless edit}}
|
||||
// STrace.LogExThrow(); {{aasim useless edit}}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines node subtree cost from existing node properties.
|
||||
/// </summary>
|
||||
/// <param name="node">Node being parsed.</param>
|
||||
/// <returns>Node subtree cost.</returns>
|
||||
protected virtual double GetNodeSubtreeCost(Node node)
|
||||
{
|
||||
// STrace.Assert(false, "GetNodeSubtreeCost should not be called because it isn't defined for all node types"); {{aasim useless edit}}
|
||||
// STrace.LogExThrow(); {{aasim useless edit}}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gets children in a generic way.
|
||||
/// It should be avoided in the cases where performance matters.
|
||||
/// </summary>
|
||||
/// <param name="parsedItem">Item to enumerate children for</param>
|
||||
/// <returns>Enumeration of children</returns>
|
||||
public static IEnumerable EnumerateChildren(object parsedItem)
|
||||
{
|
||||
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(parsedItem))
|
||||
{
|
||||
if (Type.GetTypeCode(property.PropertyType) == TypeCode.Object)
|
||||
{
|
||||
object value = property.GetValue(parsedItem);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value is IEnumerable)
|
||||
{
|
||||
foreach (object item in (IEnumerable)value)
|
||||
{
|
||||
if (XmlPlanParserFactory.GetParser(item.GetType()) != null)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (XmlPlanParserFactory.GetParser(value.GetType()) != null)
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Node.
|
||||
/// </summary>
|
||||
/// <param name="context">NodeBuilderContext.</param>
|
||||
/// <returns>New node instance.</returns>
|
||||
public static Node NewNode(NodeBuilderContext context)
|
||||
{
|
||||
XmlPlanNodeBuilder nodeBuilder = context.Context as XmlPlanNodeBuilder;
|
||||
Debug.Assert(nodeBuilder != null);
|
||||
|
||||
// We don't use "NodeId" property of the Node here because
|
||||
// not all nodes have Id and the same Id can repeat in different
|
||||
// statement branches
|
||||
return new Node(nodeBuilder.GetCurrentNodeId(), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
internal static class XmlPlanParserFactory
|
||||
{
|
||||
public static XmlPlanParser GetParser(Type type)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (type.Name)
|
||||
{
|
||||
case "RelOpType":
|
||||
return RelOpTypeParser.Instance;
|
||||
|
||||
case "BaseStmtInfoType":
|
||||
return StatementParser.Instance;
|
||||
|
||||
case "RelOpBaseType":
|
||||
return RelOpBaseTypeParser.Instance;
|
||||
|
||||
case "FilterType":
|
||||
return FilterTypeParser.Instance;
|
||||
|
||||
case "MergeType":
|
||||
return MergeTypeParser.Instance;
|
||||
|
||||
case "StmtCursorType":
|
||||
return CursorStatementParser.Instance;
|
||||
|
||||
case "CursorPlanTypeOperation":
|
||||
return CursorOperationParser.Instance;
|
||||
|
||||
case "StmtBlockType":
|
||||
case "QueryPlanType":
|
||||
case "CursorPlanType":
|
||||
case "ReceivePlanTypeOperation":
|
||||
case "StmtCondTypeThen":
|
||||
case "StmtCondTypeElse":
|
||||
return XmlPlanHierarchyParser.Instance;
|
||||
|
||||
case "StmtCondTypeCondition":
|
||||
return ConditionParser.Instance;
|
||||
|
||||
case "FunctionType":
|
||||
return FunctionTypeParser.Instance;
|
||||
|
||||
case "IndexScanType":
|
||||
case "CreateIndexType":
|
||||
return IndexOpTypeParser.Instance;
|
||||
|
||||
case "Object":
|
||||
return null;
|
||||
|
||||
default:
|
||||
type = type.BaseType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that lists String constants common to XML Show Plan Node Parsing
|
||||
/// </summary>
|
||||
public sealed class NodeBuilderConstants
|
||||
{
|
||||
public static readonly string ActualExecutions = "ActualExecutions";
|
||||
public static readonly string ActualRows = "ActualRows";
|
||||
public static readonly string Argument = "Argument";
|
||||
public static readonly string AvgRowSize = "AvgRowSize";
|
||||
public static readonly string DefinedValues = "DefinedValues";
|
||||
public static readonly string ElapsedTime = "ElapsedTime";
|
||||
public static readonly string EstimateCPU = "EstimateCPU";
|
||||
public static readonly string EstimateExecutions = "EstimateExecutions";
|
||||
public static readonly string EstimateIO = "EstimateIO";
|
||||
public static readonly string EstimateRows = "EstimateRows";
|
||||
public static readonly string LogicalOp = "LogicalOp";
|
||||
public static readonly string NodeId = "NodeId";
|
||||
public static readonly string OutputList = "OutputList";
|
||||
public static readonly string Parallel = "Parallel";
|
||||
public static readonly string ParameterCompiledValue = "ParameterCompiledValue";
|
||||
public static readonly string ParameterList = "ParameterList";
|
||||
public static readonly string ParameterRuntimeValue = "ParameterRuntimeValue";
|
||||
public static readonly string PhysicalOp = "PhysicalOp";
|
||||
public static readonly string SeekPredicate = "SeekPredicate";
|
||||
public static readonly string SeekPredicates = "SeekPredicates";
|
||||
public static readonly string StatementText = "StatementText";
|
||||
public static readonly string StatementType = "StatementType";
|
||||
public static readonly string TotalSubtreeCost = "TotalSubtreeCost";
|
||||
public static readonly string Warnings = "Warnings";
|
||||
|
||||
public static readonly string Database = "Database";
|
||||
public static readonly string Table = "Table";
|
||||
public static readonly string Schema = "Schema";
|
||||
public static readonly string Predicate = "Predicate";
|
||||
public static readonly string Storage = "Storage";
|
||||
public static readonly string Index = "Index";
|
||||
public static readonly string Object = "Object";
|
||||
|
||||
//constants for Live Nodes
|
||||
public static readonly string Status = "Status";
|
||||
public static readonly string OpenTime = "OpenTime";
|
||||
public static readonly string CompletionEstimate = "CompletionEstimate";
|
||||
public static readonly string CloseTime = "CloseTime";
|
||||
|
||||
//constants for ShowPlan Comparison
|
||||
public static readonly string SkeletonNode = "SkeletonNode";
|
||||
public static readonly string SkeletonHasMatch = "SkeletonHasMatch";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ShowPlan type
|
||||
/// </summary>
|
||||
public enum ShowPlanType
|
||||
{
|
||||
Unknown,
|
||||
Actual,
|
||||
Estimated,
|
||||
Live
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension of graph with some handy included methods specific for ShowPlan use
|
||||
/// </summary>
|
||||
public class ShowPlanGraph : Graph
|
||||
{
|
||||
private Dictionary<Node, object> nodeStmtMap = new Dictionary<Node, object>();
|
||||
|
||||
public Dictionary<Node, object> NodeStmtMap
|
||||
{
|
||||
get { return this.nodeStmtMap; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SQL Statement for this graph.
|
||||
/// </summary>
|
||||
public string Statement
|
||||
{
|
||||
get
|
||||
{
|
||||
// Special case: in the case of UDF or SP graphs thr root node doesn't
|
||||
// have StatementText. We should use Procedure Name instead
|
||||
return RootNode["StatementText"] as string ?? RootNode["ProcName"] as string ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The StatementId as recorded in the RootNode for this graph, -1 if not available
|
||||
/// </summary>
|
||||
public int StatementId
|
||||
{
|
||||
get
|
||||
{
|
||||
return PullIntFromRoot("StatementId");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The StatementCompId as recorded in the RootNode for this graph, -1 if not available
|
||||
/// </summary>
|
||||
public int StatementCompId
|
||||
{
|
||||
get
|
||||
{
|
||||
return PullIntFromRoot("StatementCompId");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The QueryPlanHash as recorded in the RootNode for this graph, null if not available
|
||||
/// </summary>
|
||||
public string QueryPlanHash
|
||||
{
|
||||
get
|
||||
{
|
||||
return RootNode["QueryPlanHash"] as string;
|
||||
}
|
||||
}
|
||||
|
||||
internal Node RootNode
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Root;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to parse an XMLString and return the set of ShowPlan graphs for it
|
||||
/// </summary>
|
||||
/// <param name="xmlString"></param>
|
||||
/// <returns></returns>
|
||||
public static ShowPlanGraph[] ParseShowPlanXML(object showPlan, ShowPlanType type = ShowPlanType.Unknown)
|
||||
{
|
||||
// Create a builder compatible with the data source
|
||||
INodeBuilder nodeBuilder = NodeBuilderFactory.Create(showPlan, type);
|
||||
|
||||
// Parse showplan data
|
||||
return nodeBuilder.Execute(showPlan);
|
||||
}
|
||||
|
||||
private int PullIntFromRoot(string name)
|
||||
{
|
||||
string statementId = RootNode[name].ToString();
|
||||
|
||||
if (statementId != null)
|
||||
{
|
||||
int id;
|
||||
if (Int32.TryParse(statementId, out id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
//error condition, return -1
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// 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;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.DataCollection.Common;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan
|
||||
{
|
||||
/// <summary>
|
||||
/// Main class for Migration Service functionality
|
||||
/// </summary>
|
||||
public sealed class ShowPlanService : IDisposable
|
||||
{
|
||||
private static readonly Lazy<ShowPlanService> instance = new Lazy<ShowPlanService>(() => new ShowPlanService());
|
||||
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new MigrationService instance with default parameters
|
||||
/// </summary>
|
||||
public ShowPlanService()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the singleton instance object
|
||||
/// </summary>
|
||||
public static ShowPlanService Instance
|
||||
{
|
||||
get { return instance.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service host object for sending/receiving requests/events.
|
||||
/// Internal for testing purposes.
|
||||
/// </summary>
|
||||
internal IProtocolEndpoint ServiceHost
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the ShowPlan Service instance
|
||||
/// </summary>
|
||||
public void InitializeService(ServiceHost serviceHost)
|
||||
{
|
||||
this.ServiceHost = serviceHost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the ShowPlan Service
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,8 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<Content Remove=".\ShowPlan\TestExecution.xml" />
|
||||
<EmbeddedResource Include=".\ShowPlan\TestExecutionPlan.xml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph;
|
||||
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
|
||||
{
|
||||
public class ShowPlanXMLTests
|
||||
{
|
||||
[Test]
|
||||
public async Task ParseXMLFileReturnsValidShowPlanGraph()
|
||||
{
|
||||
Assembly assembly = Assembly.GetAssembly(typeof(ShowPlanXMLTests));
|
||||
Stream scriptStream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".ShowPlan.TestExecutionPlan.xml");
|
||||
StreamReader reader = new StreamReader(scriptStream);
|
||||
string text = reader.ReadToEnd();
|
||||
var showPlanGraphs = ShowPlanGraph.ParseShowPlanXML(text, ShowPlanType.Actual);
|
||||
Assert.AreEqual(1, showPlanGraphs.Length, "Single show plan graph not generated from the test xml file");
|
||||
var testShowPlanGraph = showPlanGraphs[0];
|
||||
Assert.NotNull(testShowPlanGraph, "graph should not be null");
|
||||
Assert.NotNull(testShowPlanGraph.Root, "graph should have a root");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user