in src/Microsoft.SqlTools.ServiceLayer/ShowPlan/ShowPlanGraph/RelOpTypeParser.cs [76:190]
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;
}