Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<docs>
<docs>
<members name="SqlMetaData">
<SqlMetaData>
<summary>
Expand Down Expand Up @@ -1490,6 +1490,22 @@
The <paramref name="value" /> is <see langword="null" />.
</exception>
</InferFromValue>
<IsComputed>
<summary>
Indicates if this column is computed by the server.
</summary>
<value>
A <see cref="T:System.Boolean" /> value.
</value>
<remarks>
<para>
The default is <see langword="false" />.
</para>
<para>
This property can only be set from an object initializer immediately after construction of the <see cref="T:Microsoft.Data.SqlClient.Server.SqlMetaData" /> instance.
</para>
</remarks>
</IsComputed>
<IsUniqueKey>
<summary>
Indicates if the column in the table-valued parameter is unique.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

Expand Down Expand Up @@ -215,6 +215,8 @@ public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDe
public System.Data.SqlTypes.SqlCompareOptions CompareOptions { get { throw null; } }
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/DbType/*' />
public System.Data.DbType DbType { get { throw null; } }
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/IsComputed/*' />
public bool IsComputed { get { throw null; } init { } }
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/IsUniqueKey/*' />
public bool IsUniqueKey { get { throw null; } }
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/LocaleId/*' />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if !NET

namespace System.Runtime.CompilerServices;

/// <summary>
/// Reserved to be used by the compiler for tracking metadata.
/// This class should not be used by developers in source code.
/// </summary>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
internal static class IsExternalInit
{
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ SmiMetaDataPropertyCollection extendedProperties
((SmiDefaultFieldsProperty)_extendedProperties[SmiPropertySelector.DefaultFields]).CheckCount(_fieldMetaData.Count);
((SmiOrderProperty)_extendedProperties[SmiPropertySelector.SortOrder]).CheckCount(_fieldMetaData.Count);
((SmiUniqueKeyProperty)_extendedProperties[SmiPropertySelector.UniqueKey]).CheckCount(_fieldMetaData.Count);
((SmiComputedFieldsProperty)_extendedProperties[SmiPropertySelector.ComputedFields]).CheckCount(_fieldMetaData.Count);
#endif
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ internal enum SmiPropertySelector
DefaultFields = 0x0,
SortOrder = 0x1,
UniqueKey = 0x2,
ComputedFields = 0x3,
}

// Simple collection for properties. Could extend to IDictionary support if needed in future.
internal class SmiMetaDataPropertyCollection
{
private const int SelectorCount = 3; // number of elements in SmiPropertySelector
private const int SelectorCount = 4; // number of elements in SmiPropertySelector

private readonly SmiMetaDataProperty[] _properties;
private bool _isReadOnly;
Expand All @@ -32,6 +33,7 @@ internal class SmiMetaDataPropertyCollection
private static readonly SmiDefaultFieldsProperty s_emptyDefaultFields = new SmiDefaultFieldsProperty(new List<bool>());
private static readonly SmiOrderProperty s_emptySortOrder = new SmiOrderProperty(new List<SmiOrderProperty.SmiColumnOrder>());
private static readonly SmiUniqueKeyProperty s_emptyUniqueKey = new SmiUniqueKeyProperty(new List<bool>());
private static readonly SmiComputedFieldsProperty s_emptyIsComputedField = new SmiComputedFieldsProperty(new List<bool>());

internal static readonly SmiMetaDataPropertyCollection s_emptyInstance = CreateEmptyInstance();

Expand All @@ -49,6 +51,7 @@ internal SmiMetaDataPropertyCollection()
_properties[(int)SmiPropertySelector.DefaultFields] = s_emptyDefaultFields;
_properties[(int)SmiPropertySelector.SortOrder] = s_emptySortOrder;
_properties[(int)SmiPropertySelector.UniqueKey] = s_emptyUniqueKey;
_properties[(int)SmiPropertySelector.ComputedFields] = s_emptyIsComputedField;
}

internal SmiMetaDataProperty this[SmiPropertySelector key]
Expand Down Expand Up @@ -275,4 +278,67 @@ internal override string TraceString()

#endregion
}

internal class SmiComputedFieldsProperty : SmiMetaDataProperty
{
#region private fields

private readonly IList<bool> _computed;

#endregion

#region internal interface

internal SmiComputedFieldsProperty(IList<bool> computedFields) => _computed = new System.Collections.ObjectModel.ReadOnlyCollection<bool>(computedFields);

internal bool this[int ordinal]
{
get
{
if (_computed.Count <= ordinal)
{
return false;
}
else
{
return _computed[ordinal];
}
}
}

[Conditional("DEBUG")]
internal void CheckCount(int countToMatch)
{
Debug.Assert(0 == _computed.Count || countToMatch == _computed.Count,
"SmiComputedFieldsProperty.CheckCount: ComputedFieldsProperty size (" + _computed.Count +
") not equal to checked size (" + countToMatch + ")");
}

internal override string TraceString()
{
string returnValue = "ComputedFields(";
bool delimit = false;
for (int columnOrd = 0; columnOrd < _computed.Count; columnOrd++)
{
if (delimit)
{
returnValue += ",";
}
else
{
delimit = true;
}

if (_computed[columnOrd])
{
returnValue += columnOrd;
}
}
returnValue += ")";

return returnValue;
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

Expand Down Expand Up @@ -505,6 +505,9 @@ public string TypeName
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/UseServerDefault/*' />
public bool UseServerDefault => _useServerDefault;

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/IsComputed/*' />
public bool IsComputed { get; init; }

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient.Server/SqlMetaData.xml' path='docs/members[@name="SqlMetaData"]/XmlSchemaCollectionDatabase/*' />
public string XmlSchemaCollectionDatabase => _xmlSchemaCollectionDatabase;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1334,9 +1334,11 @@ private void GetActualFieldsAndProperties(out List<SmiExtendedMetaData> fields,
bool[] keyCols = new bool[fieldCount];
bool[] defaultFields = new bool[fieldCount];
bool[] sortOrdinalSpecified = new bool[fieldCount];
bool[] computedFields = new bool[fieldCount];
int maxSortOrdinal = -1; // largest sort ordinal seen, used to optimize locating holes in the list
bool hasKey = false;
bool hasDefault = false;
bool hasComputedFields = false;
int sortCount = 0;
SmiOrderProperty.SmiColumnOrder[] sort = new SmiOrderProperty.SmiColumnOrder[fieldCount];
fields = new List<SmiExtendedMetaData>(fieldCount);
Expand All @@ -1356,6 +1358,16 @@ private void GetActualFieldsAndProperties(out List<SmiExtendedMetaData> fields,
hasDefault = true;
}

if (colMeta.IsComputed)
{
// A computed column will always have a default value, so skip writing
// its values out to the TDS stream.
defaultFields[i] = true;
computedFields[i] = true;
hasDefault = true;
hasComputedFields = true;
}

sort[i]._order = colMeta.SortOrder;
if (SortOrder.Unspecified != colMeta.SortOrder)
{
Expand Down Expand Up @@ -1400,6 +1412,14 @@ private void GetActualFieldsAndProperties(out List<SmiExtendedMetaData> fields,
props[SmiPropertySelector.DefaultFields] = new SmiDefaultFieldsProperty(new List<bool>(defaultFields));
}

if (hasComputedFields)
{
// We've already created props list in default value handling
Debug.Assert(props is not null);

props[SmiPropertySelector.ComputedFields] = new SmiComputedFieldsProperty(new List<bool>(computedFields));
}

if (0 < sortCount)
{
// validate monotonically increasing sort order.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ public enum ActiveDirectoryWorkflow : byte
public const byte TVP_ORDER_UNIQUE_TOKEN = 0x10;

// TvpColumnMetaData flags
public const int TVP_COMPUTED_COLUMN = 0x20;
public const int TVP_DEFAULT_COLUMN = 0x200;

// TVP_ORDER_UNIQUE_TOKEN flags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11261,9 +11261,10 @@ private void WriteTvpTypeInfo(SmiExtendedMetaData metaData, TdsParserStateObject

// TvpColumnMetaData for each column (look for defaults in this loop
SmiDefaultFieldsProperty defaults = (SmiDefaultFieldsProperty)metaData.ExtendedProperties[SmiPropertySelector.DefaultFields];
SmiComputedFieldsProperty computedFields = (SmiComputedFieldsProperty)metaData.ExtendedProperties[SmiPropertySelector.ComputedFields];
for (int i = 0; i < metaData.FieldMetaData.Count; i++)
{
WriteTvpColumnMetaData(metaData.FieldMetaData[i], defaults[i], stateObj);
WriteTvpColumnMetaData(metaData.FieldMetaData[i], defaults[i], computedFields[i], stateObj);
}

// optional OrderUnique metadata
Expand All @@ -11275,7 +11276,7 @@ private void WriteTvpTypeInfo(SmiExtendedMetaData metaData, TdsParserStateObject
}

// Write a single TvpColumnMetaData stream to the server
private void WriteTvpColumnMetaData(SmiExtendedMetaData md, bool isDefault, TdsParserStateObject stateObj)
private void WriteTvpColumnMetaData(SmiExtendedMetaData md, bool isDefault, bool isComputed, TdsParserStateObject stateObj)
{
// User Type
if (SqlDbType.Timestamp == md.SqlDbType)
Expand All @@ -11293,6 +11294,10 @@ private void WriteTvpColumnMetaData(SmiExtendedMetaData md, bool isDefault, TdsP
{
status |= TdsEnums.TVP_DEFAULT_COLUMN;
}
if (isComputed)
{
status |= TdsEnums.TVP_COMPUTED_COLUMN;
}
WriteUnsignedShort(status, stateObj);

// Type info
Expand Down
Loading
Loading