mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -05:00
Handling the HierarchyId for edit data scenario (#709)
* Handling the HierarchyId for edit data scenario * comments
This commit is contained in:
@@ -36,6 +36,22 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
/// </summary>
|
||||
public string EscapedName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The column's expression for select statement
|
||||
/// </summary>
|
||||
public string ExpressionForSelectStatement
|
||||
{
|
||||
get
|
||||
{
|
||||
return IsHierarchyId ? string.Format("{0}.ToString() AS {0}", EscapedName) : EscapedName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the column's data type is HierarchyId
|
||||
/// </summary>
|
||||
public bool IsHierarchyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the column is computed
|
||||
/// </summary>
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Collections.Concurrent;
|
||||
using System.Data.Common;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
@@ -149,11 +150,12 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
internal async Task HandleInitializeRequest(EditInitializeParams initParams,
|
||||
RequestContext<EditInitializeResult> requestContext)
|
||||
{
|
||||
Func<Exception, Task> executionFailureHandler = (e) => SendSessionReadyEvent(requestContext, initParams.OwnerUri, false, e.Message);
|
||||
Func<Task> executionSuccessHandler = () => SendSessionReadyEvent(requestContext, initParams.OwnerUri, true, null);
|
||||
InitializeEditRequestContext context = new InitializeEditRequestContext(requestContext);
|
||||
Func<Exception, Task> executionFailureHandler = (e) => SendSessionReadyEvent(context, initParams.OwnerUri, false, e.Message);
|
||||
Func<Task> executionSuccessHandler = () => SendSessionReadyEvent(context, initParams.OwnerUri, true, null);
|
||||
|
||||
EditSession.Connector connector = () => connectionService.GetOrOpenConnection(initParams.OwnerUri, ConnectionType.Edit, alwaysPersistSecurity: true);
|
||||
EditSession.QueryRunner queryRunner = q => SessionInitializeQueryRunner(initParams.OwnerUri, requestContext, q);
|
||||
EditSession.QueryRunner queryRunner = q => SessionInitializeQueryRunner(initParams.OwnerUri, context, q);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -169,6 +171,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
throw new InvalidOperationException(SR.EditDataSessionAlreadyExists);
|
||||
}
|
||||
|
||||
context.ResultSetHandler = (ResultSetEventParams resultSetEventParams) => { session.UpdateColumnInformationWithMetadata(resultSetEventParams.ResultSetSummary.ColumnInfo); };
|
||||
|
||||
// Initialize the session
|
||||
session.Initialize(initParams, connector, queryRunner, executionSuccessHandler, executionFailureHandler);
|
||||
|
||||
@@ -214,7 +218,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
await requestContext.SendError(e.Message);
|
||||
}
|
||||
@@ -341,4 +345,29 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context for InitializeEditRequest, to provide a way to update the result set before sending it to UI.
|
||||
/// </summary>
|
||||
internal class InitializeEditRequestContext : IEventSender
|
||||
{
|
||||
private RequestContext<EditInitializeResult> _context;
|
||||
|
||||
public Action<ResultSetEventParams> ResultSetHandler { get; set; }
|
||||
|
||||
public InitializeEditRequestContext(RequestContext<EditInitializeResult> context)
|
||||
{
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
public Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
|
||||
{
|
||||
if (eventParams is ResultSetEventParams && this.ResultSetHandler != null)
|
||||
{
|
||||
this.ResultSetHandler(eventParams as ResultSetEventParams);
|
||||
}
|
||||
return _context.SendEvent(eventType, eventParams);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
/// </summary>
|
||||
public static void CheckResultsForInvalidColumns(ResultSet results, string tableName)
|
||||
{
|
||||
if(SchemaContainsMultipleItems(results.Columns, col => col.BaseCatalogName)
|
||||
if (SchemaContainsMultipleItems(results.Columns, col => col.BaseCatalogName)
|
||||
|| SchemaContainsMultipleItems(results.Columns, col => col.BaseSchemaName)
|
||||
|| SchemaContainsMultipleItems(results.Columns, col => col.BaseTableName))
|
||||
{
|
||||
@@ -168,7 +168,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
throw new InvalidOperationException(SR.EditDataAliasesNotSupported);
|
||||
}
|
||||
|
||||
if (col.IsExpression.HasTrue())
|
||||
// We have changed HierarchyId column to an expression so that it can be displayed properly
|
||||
if (!col.IsHierarchyId && col.IsExpression.HasTrue())
|
||||
{
|
||||
throw new InvalidOperationException(SR.EditDataExpressionsNotSupported);
|
||||
}
|
||||
@@ -480,6 +481,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
|
||||
// Step 3) Setup the internal state
|
||||
associatedResultSet = ValidateQueryForSession(state.Query);
|
||||
UpdateColumnInformationWithMetadata(associatedResultSet.Columns);
|
||||
CheckResultsForInvalidColumns(associatedResultSet, initParams.ObjectName);
|
||||
|
||||
NextRowId = associatedResultSet.RowCount;
|
||||
@@ -499,7 +501,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
public static string[] GetEditTargetName(EditInitializeParams initParams)
|
||||
{
|
||||
return initParams.SchemaName != null
|
||||
? new [] { initParams.SchemaName, initParams.ObjectName }
|
||||
? new[] { initParams.SchemaName, initParams.ObjectName }
|
||||
: FromSqlScript.DecodeMultipartIdentifier(initParams.ObjectName);
|
||||
}
|
||||
|
||||
@@ -508,7 +510,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
try
|
||||
{
|
||||
// @TODO: Add support for transactional commits
|
||||
|
||||
|
||||
// Trust the RowEdit to sort itself appropriately
|
||||
var editOperations = EditCache.Values.ToList();
|
||||
editOperations.Sort();
|
||||
@@ -554,7 +556,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
}
|
||||
|
||||
// Using the columns we know, add them to the query
|
||||
var columns = metadata.Columns.Select(col => col.EscapedName);
|
||||
var columns = metadata.Columns.Select(col => col.ExpressionForSelectStatement);
|
||||
var columnClause = string.Join(", ", columns);
|
||||
queryBuilder.Append(columnClause);
|
||||
|
||||
@@ -590,6 +592,20 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
EditCache.TryRemove(rowId, out removedRow);
|
||||
}
|
||||
|
||||
internal void UpdateColumnInformationWithMetadata(DbColumnWrapper[] columns)
|
||||
{
|
||||
if (columns == null || this.objectMetadata == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (DbColumnWrapper col in columns)
|
||||
{
|
||||
var columnMetadata = objectMetadata.Columns.FirstOrDefault(cm => { return cm.EscapedName == ToSqlScript.FormatIdentifier(col.ColumnName); });
|
||||
col.IsHierarchyId = columnMetadata != null && columnMetadata.IsHierarchyId;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
|
||||
DefaultValue = defaultValue,
|
||||
EscapedName = ToSqlScript.FormatIdentifier(smoColumn.Name),
|
||||
Ordinal = i,
|
||||
IsHierarchyId = smoColumn.DataType.SqlDataType == SqlDataType.HierarchyId,
|
||||
};
|
||||
editColumns.Add(column);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
: base(rowId, associatedResultSet, associatedMetadata)
|
||||
{
|
||||
newCells = new CellUpdate[AssociatedResultSet.Columns.Length];
|
||||
|
||||
|
||||
// Process the default cell values. If the column is calculated, then the value is a placeholder
|
||||
DefaultValues = AssociatedObjectMetadata.Columns.Select((col, index) => col.IsCalculated.HasTrue()
|
||||
? SR.EditDataComputedColumnPlaceholder
|
||||
@@ -61,7 +61,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
/// provided cell update during commit
|
||||
/// </summary>
|
||||
public string[] DefaultValues { get; }
|
||||
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
@@ -96,14 +96,21 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
List<string> inValues = new List<string>();
|
||||
List<SqlParameter> inParameters = new List<SqlParameter>();
|
||||
List<string> selectColumns = new List<string>();
|
||||
for(int i = 0; i < AssociatedObjectMetadata.Columns.Length; i++)
|
||||
for (int i = 0; i < AssociatedObjectMetadata.Columns.Length; i++)
|
||||
{
|
||||
DbColumnWrapper column = AssociatedResultSet.Columns[i];
|
||||
EditColumnMetadata metadata = AssociatedObjectMetadata.Columns[i];
|
||||
CellUpdate cell = newCells[i];
|
||||
|
||||
|
||||
// Add the output columns regardless of whether the column is read only
|
||||
outClauseColumnNames.Add($"inserted.{metadata.EscapedName}");
|
||||
if (metadata.IsHierarchyId)
|
||||
{
|
||||
outClauseColumnNames.Add($"inserted.{metadata.EscapedName}.ToString() {metadata.EscapedName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
outClauseColumnNames.Add($"inserted.{metadata.EscapedName}");
|
||||
}
|
||||
declareColumns.Add($"{metadata.EscapedName} {ToSqlScript.FormatColumnType(column, useSemanticEquivalent: true)}");
|
||||
selectColumns.Add(metadata.EscapedName);
|
||||
|
||||
@@ -112,16 +119,25 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Add the input column
|
||||
inColumnNames.Add(metadata.EscapedName);
|
||||
|
||||
|
||||
// Add the input values as parameters
|
||||
string paramName = $"@Value{RowId}_{i}";
|
||||
inValues.Add(paramName);
|
||||
inParameters.Add(new SqlParameter(paramName, column.SqlDbType) {Value = cell.Value});
|
||||
|
||||
if (metadata.IsHierarchyId)
|
||||
{
|
||||
inValues.Add($"CONVERT(hierarchyid,{paramName})");
|
||||
}
|
||||
else
|
||||
{
|
||||
inValues.Add(paramName);
|
||||
}
|
||||
|
||||
inParameters.Add(new SqlParameter(paramName, column.SqlDbType) { Value = cell.Value });
|
||||
}
|
||||
|
||||
|
||||
// Put everything together into a single query
|
||||
// Step 1) Build a temp table for inserting output values into
|
||||
string tempTableName = $"@Insert{RowId}Output";
|
||||
@@ -130,32 +146,32 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
// Step 2) Build the insert statement
|
||||
string joinedOutClauseNames = string.Join(", ", outClauseColumnNames);
|
||||
string insertStatement = inValues.Count > 0
|
||||
? string.Format(InsertOutputValuesStatement,
|
||||
? string.Format(InsertOutputValuesStatement,
|
||||
AssociatedObjectMetadata.EscapedMultipartName,
|
||||
string.Join(", ", inColumnNames),
|
||||
string.Join(", ", inColumnNames),
|
||||
joinedOutClauseNames,
|
||||
tempTableName,
|
||||
string.Join(", ", inValues))
|
||||
: string.Format(InsertOutputDefaultStatement,
|
||||
: string.Format(InsertOutputDefaultStatement,
|
||||
AssociatedObjectMetadata.EscapedMultipartName,
|
||||
joinedOutClauseNames,
|
||||
tempTableName);
|
||||
|
||||
// Step 3) Build the select statement
|
||||
string selectStatement = string.Format(SelectStatement, string.Join(", ", selectColumns), tempTableName);
|
||||
|
||||
|
||||
// Step 4) Put it all together into a results object
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.AppendLine(declareStatement);
|
||||
query.AppendLine(insertStatement);
|
||||
query.Append(selectStatement);
|
||||
|
||||
|
||||
// Build the command
|
||||
DbCommand command = connection.CreateCommand();
|
||||
command.CommandText = query.ToString();
|
||||
command.CommandType = CommandType.Text;
|
||||
command.Parameters.AddRange(inParameters.ToArray());
|
||||
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@@ -168,7 +184,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
{
|
||||
// Get edit cells for each
|
||||
EditCell[] editCells = newCells.Select(GetEditCell).ToArray();
|
||||
|
||||
|
||||
return new EditRow
|
||||
{
|
||||
Id = RowId,
|
||||
@@ -190,23 +206,23 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
{
|
||||
DbColumnWrapper column = AssociatedResultSet.Columns[i];
|
||||
CellUpdate cell = newCells[i];
|
||||
|
||||
|
||||
// Continue if we're not inserting a value for this column
|
||||
if (!IsCellValueProvided(column, cell, DefaultValues[i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Column is provided
|
||||
inColumns.Add(AssociatedObjectMetadata.Columns[i].EscapedName);
|
||||
inValues.Add(ToSqlScript.FormatValue(cell.AsDbCellValue, column));
|
||||
}
|
||||
|
||||
|
||||
// Build the insert statement
|
||||
return inValues.Count > 0
|
||||
? string.Format(InsertScriptValuesStatement,
|
||||
? string.Format(InsertScriptValuesStatement,
|
||||
AssociatedObjectMetadata.EscapedMultipartName,
|
||||
string.Join(", ", inColumns),
|
||||
string.Join(", ", inColumns),
|
||||
string.Join(", ", inValues))
|
||||
: string.Format(InsertScriptDefaultStatement, AssociatedObjectMetadata.EscapedMultipartName);
|
||||
}
|
||||
@@ -225,7 +241,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
newCells[columnId] = null;
|
||||
return new EditRevertCellResult
|
||||
{
|
||||
IsRowDirty = true,
|
||||
IsRowDirty = true,
|
||||
Cell = GetEditCell(null, columnId)
|
||||
};
|
||||
}
|
||||
@@ -277,7 +293,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Make sure a value was provided for the cell
|
||||
if (cell == null)
|
||||
{
|
||||
@@ -286,14 +302,14 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
{
|
||||
throw new InvalidOperationException(SR.EditDataCreateScriptMissingValue(column.ColumnName));
|
||||
}
|
||||
|
||||
|
||||
// There is a default value (or omitting the value is fine), so trust the db will apply it correctly
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private EditCell GetEditCell(CellUpdate cell, int index)
|
||||
{
|
||||
DbCellValue dbCell;
|
||||
|
||||
@@ -97,32 +97,46 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
for (int i = 0; i < AssociatedObjectMetadata.Columns.Length; i++)
|
||||
{
|
||||
EditColumnMetadata metadata = AssociatedObjectMetadata.Columns[i];
|
||||
|
||||
|
||||
// Add the output columns regardless of whether the column is read only
|
||||
declareColumns.Add($"{metadata.EscapedName} {ToSqlScript.FormatColumnType(metadata.DbColumn, useSemanticEquivalent: true)}");
|
||||
outClauseColumns.Add($"inserted.{metadata.EscapedName}");
|
||||
if (metadata.IsHierarchyId)
|
||||
{
|
||||
outClauseColumns.Add($"inserted.{metadata.EscapedName}.ToString() {metadata.EscapedName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
outClauseColumns.Add($"inserted.{metadata.EscapedName}");
|
||||
}
|
||||
selectColumns.Add(metadata.EscapedName);
|
||||
|
||||
|
||||
// If we have a new value for the column, proccess it now
|
||||
CellUpdate cellUpdate;
|
||||
if (cellUpdates.TryGetValue(i, out cellUpdate))
|
||||
{
|
||||
string paramName = $"@Value{RowId}_{i}";
|
||||
setComponents.Add($"{metadata.EscapedName} = {paramName}");
|
||||
inParameters.Add(new SqlParameter(paramName, AssociatedResultSet.Columns[i].SqlDbType) {Value = cellUpdate.Value});
|
||||
if (metadata.IsHierarchyId)
|
||||
{
|
||||
setComponents.Add($"{metadata.EscapedName} = CONVERT(hierarchyid,{paramName})");
|
||||
}
|
||||
else
|
||||
{
|
||||
setComponents.Add($"{metadata.EscapedName} = {paramName}");
|
||||
}
|
||||
inParameters.Add(new SqlParameter(paramName, AssociatedResultSet.Columns[i].SqlDbType) { Value = cellUpdate.Value });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Put everything together into a single query
|
||||
// Step 1) Build a temp table for inserting output values into
|
||||
string tempTableName = $"@Update{RowId}Output";
|
||||
string declareStatement = string.Format(DeclareStatement, tempTableName, string.Join(", ", declareColumns));
|
||||
|
||||
|
||||
// Step 2) Build the update statement
|
||||
WhereClause whereClause = GetWhereClause(true);
|
||||
|
||||
string updateStatementFormat = AssociatedObjectMetadata.IsMemoryOptimized
|
||||
? UpdateOutputMemOptimized
|
||||
|
||||
string updateStatementFormat = AssociatedObjectMetadata.IsMemoryOptimized
|
||||
? UpdateOutputMemOptimized
|
||||
: UpdateOutput;
|
||||
string updateStatement = string.Format(updateStatementFormat,
|
||||
AssociatedObjectMetadata.EscapedMultipartName,
|
||||
@@ -135,10 +149,10 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
string validateScript = string.Format(CultureInfo.InvariantCulture, validateUpdateOnlyOneRow,
|
||||
AssociatedObjectMetadata.EscapedMultipartName,
|
||||
whereClause.CommandText);
|
||||
|
||||
|
||||
// Step 3) Build the select statement
|
||||
string selectStatement = string.Format(SelectStatement, string.Join(", ", selectColumns), tempTableName);
|
||||
|
||||
|
||||
// Step 4) Put it all together into a results object
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.AppendLine(declareStatement);
|
||||
@@ -146,7 +160,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
query.AppendLine(updateStatement);
|
||||
query.AppendLine(selectStatement);
|
||||
query.Append("END");
|
||||
|
||||
|
||||
// Build the command
|
||||
DbCommand command = connection.CreateCommand();
|
||||
command.CommandText = query.ToString();
|
||||
@@ -198,7 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
return $"{formattedColumnName} = {formattedValue}";
|
||||
});
|
||||
string setClause = string.Join(", ", setComponents);
|
||||
|
||||
|
||||
// Put everything together into a single query
|
||||
string whereClause = GetWhereClause(false).CommandText;
|
||||
string updateStatementFormat = AssociatedObjectMetadata.IsMemoryOptimized
|
||||
@@ -247,7 +261,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
|
||||
public override EditUpdateCellResult SetCell(int columnId, string newValue)
|
||||
{
|
||||
// Validate the value and convert to object
|
||||
ValidateColumnIsUpdatable(columnId);
|
||||
ValidateColumnIsUpdatable(columnId);
|
||||
CellUpdate update = new CellUpdate(AssociatedResultSet.Columns[columnId], newValue);
|
||||
|
||||
// If the value is the same as the old value, we shouldn't make changes
|
||||
|
||||
@@ -199,8 +199,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
|
||||
object assemblyQualifiedName = column.UdtAssemblyQualifiedName;
|
||||
const string hierarchyId = "MICROSOFT.SQLSERVER.TYPES.SQLHIERARCHYID";
|
||||
|
||||
if (assemblyQualifiedName != null &&
|
||||
string.Equals(assemblyQualifiedName.ToString(), hierarchyId, StringComparison.OrdinalIgnoreCase))
|
||||
if (assemblyQualifiedName != null
|
||||
&& assemblyQualifiedName.ToString().StartsWith(hierarchyId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
DataType = typeof(SqlBinary);
|
||||
}
|
||||
@@ -259,6 +259,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
|
||||
/// </summary>
|
||||
public SqlDbType SqlDbType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whther this is a HierarchyId column
|
||||
/// </summary>
|
||||
public bool IsHierarchyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the column is an XML Reader type.
|
||||
/// </summary>
|
||||
@@ -286,10 +291,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Logic taken from SSDT determination of updatable columns
|
||||
/// Special treatment for HierarchyId since we are using an Expression for HierarchyId column and expression column is readonly.
|
||||
/// </remarks>
|
||||
public bool IsUpdatable => !IsAutoIncrement.HasTrue() &&
|
||||
!IsReadOnly.HasTrue() &&
|
||||
!IsSqlXmlType;
|
||||
public bool IsUpdatable => (!IsAutoIncrement.HasTrue() &&
|
||||
!IsReadOnly.HasTrue() &&
|
||||
!IsSqlXmlType) || IsHierarchyId;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
Reference in New Issue
Block a user