diff --git a/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj b/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj index b038d5b..2ce1b9c 100644 --- a/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj +++ b/src/Microsoft.SqlServer.Types.Tests/Microsoft.SqlServer.Types.Tests.csproj @@ -1,7 +1,7 @@  - net6.0;net461 + net7.0;net6.0;net461 false enable 10.0 diff --git a/src/Microsoft.SqlServer.Types/ISqlSpatialGridIndexable.cs b/src/Microsoft.SqlServer.Types/ISqlSpatialGridIndexable.cs index 2718354..4c28cb5 100644 --- a/src/Microsoft.SqlServer.Types/ISqlSpatialGridIndexable.cs +++ b/src/Microsoft.SqlServer.Types/ISqlSpatialGridIndexable.cs @@ -1,4 +1,6 @@ -namespace Microsoft.SqlServer.Types +using Microsoft.SqlServer.Server; + +namespace Microsoft.SqlServer.Types { /// /// This API supports the product infrastructure and is not intended to be used directly from your code. diff --git a/src/Microsoft.SqlServer.Types/Microsoft.SqlServer.Types.csproj b/src/Microsoft.SqlServer.Types/Microsoft.SqlServer.Types.csproj index 1fc0e6f..83de11d 100644 --- a/src/Microsoft.SqlServer.Types/Microsoft.SqlServer.Types.csproj +++ b/src/Microsoft.SqlServer.Types/Microsoft.SqlServer.Types.csproj @@ -9,7 +9,7 @@ true Debug;Release;Debug_Legacy;Release_Legacy true - 5.0 + 6.0 2.$(MinorVersion) 1.$(MinorVersion) @@ -47,7 +47,7 @@ - 4.0.0 + 5.0.1 @@ -56,9 +56,9 @@ - - - + + + @@ -77,10 +77,8 @@ - - + + diff --git a/src/Microsoft.SqlServer.Types/SqlGeography.cs b/src/Microsoft.SqlServer.Types/SqlGeography.cs index 06dd5be..aed5553 100644 --- a/src/Microsoft.SqlServer.Types/SqlGeography.cs +++ b/src/Microsoft.SqlServer.Types/SqlGeography.cs @@ -1,3 +1,5 @@ +using Microsoft.SqlServer.Server; + namespace Microsoft.SqlServer.Types { /// @@ -11,10 +13,10 @@ public class SqlGeography : INullable, IBinarySerialize internal SqlGeography(bool isNull) { IsNull = isNull; } - internal SqlGeography(ShapeData g, int srid) - { - this.srid = srid; - this._geometry = g; + internal SqlGeography(ShapeData g, int srid) + { + this.srid = srid; + this._geometry = g; } /// @@ -170,14 +172,14 @@ public static SqlGeography Null { /// This method will return NULL if this is not a Polygon instance and will return 0 if the instance is empty. /// /// In the SQL Server geography type, external and internal rings are not distinguished, as any ring can be taken to be the external ring. - [SqlMethod(IsDeterministic = true, IsPrecise = true)] - public SqlInt32 NumRings() + [SqlMethod(IsDeterministic = true, IsPrecise = true)] + public SqlInt32 NumRings() { if (IsNull || (_geometry.Type != OGCGeometryType.Polygon && _geometry.Type != OGCGeometryType.CurvePolygon)) - { - return SqlInt32.Null; - } - return this._geometry.NumRings; + { + return SqlInt32.Null; + } + return this._geometry.NumRings; } /// @@ -225,19 +227,19 @@ public SqlInt32 STNumCurves() { if (IsNull || _geometry.IsEmpty) return SqlInt32.Null; - if (_geometry.Type == OGCGeometryType.LineString) - return _geometry.IsEmpty ? 0 : _geometry.NumPoints - 1; + if (_geometry.Type == OGCGeometryType.LineString) + return _geometry.IsEmpty ? 0 : _geometry.NumPoints - 1; - if (_geometry.Type == OGCGeometryType.CircularString) + if (_geometry.Type == OGCGeometryType.CircularString) { if (_geometry.IsEmpty) return 0; return (_geometry.NumPoints - 1) / 2; } if(_geometry.Type == OGCGeometryType.Polygon) - return _geometry.NumRings; + return _geometry.NumRings; if (_geometry.Type != OGCGeometryType.CompoundCurve) return SqlInt32.Null; - return _geometry.NumRings; + return _geometry.NumRings; } /// @@ -252,7 +254,7 @@ public SqlInt32 STNumCurves() [SqlMethod(IsDeterministic = true, IsPrecise = true)] public SqlGeography STGeometryN(int n) { - if (n < 1) + if (n < 1) throw new ArgumentOutOfRangeException(nameof(n)); if (IsNull || n > STNumGeometries()) @@ -291,24 +293,24 @@ public SqlGeography STCurveN(int n) /// public SqlGeography STPointN(int n) { - if (n < 1) - throw new ArgumentOutOfRangeException(nameof(n)); - if (IsNull) - return SqlGeography.Null; - - if (n > this._geometry.NumPoints) - return SqlGeography.Null; - - var p = _geometry.GetPointN(n); + if (n < 1) + throw new ArgumentOutOfRangeException(nameof(n)); + if (IsNull) + return SqlGeography.Null; + + if (n > this._geometry.NumPoints) + return SqlGeography.Null; + + var p = _geometry.GetPointN(n); return new SqlGeography(new ShapeData(p.X, p.Y, HasZ ? (double?)p.Z : null, HasM ? (double?)p.M : null), srid); } - + /// /// Returns the start point of a SqlGeography instance. /// - /// A SqlGeography value that represents the start point of the calling SqlGeography. - /// STStartPoint is the equivalent of STPointN(1). - [SqlMethod(IsDeterministic = true, IsPrecise = true)] + /// A SqlGeography value that represents the start point of the calling SqlGeography. + /// STStartPoint is the equivalent of STPointN(1). + [SqlMethod(IsDeterministic = true, IsPrecise = true)] public SqlGeography STStartPoint() => this.STPointN(1); // public SqlGeography STUnion(SqlGeography sqlGeography) @@ -324,8 +326,8 @@ public SqlGeography STPointN(int n) /// STEndPoint is the equivalent of SqlGeography.STPointN(x.STNumPoints()). /// This method returns null if called on an empty geography instance. /// - [SqlMethod(IsDeterministic = true, IsPrecise = true)] - public SqlGeography STEndPoint() => STPointN(Math.Max(1, _geometry.NumPoints)); + [SqlMethod(IsDeterministic = true, IsPrecise = true)] + public SqlGeography STEndPoint() => STPointN(Math.Max(1, _geometry.NumPoints)); /// /// Returns the specified ring of the SqlGeography instance: 1 ≤ n ≤ NumRings(). @@ -338,14 +340,14 @@ public SqlGeography STPointN(int n) [SqlMethodAttribute(IsDeterministic = true, IsPrecise = true)] public SqlGeography RingN(int n) { - if (n < 1) + if (n < 1) throw new ArgumentOutOfRangeException(nameof(n)); - if (IsNull || (_geometry.Type != OGCGeometryType.Polygon && _geometry.Type != OGCGeometryType.CurvePolygon) || n > this._geometry.NumRings) - { - return SqlGeography.Null; - } - ShapeData ring = _geometry.GetRing(n - 1); - ring.SetIsValid(false); + if (IsNull || (_geometry.Type != OGCGeometryType.Polygon && _geometry.Type != OGCGeometryType.CurvePolygon) || n > this._geometry.NumRings) + { + return SqlGeography.Null; + } + ShapeData ring = _geometry.GetRing(n - 1); + ring.SetIsValid(false); return new SqlGeography(ring, this.srid); } @@ -356,8 +358,8 @@ public SqlGeography RingN(int n) [SqlMethodAttribute(IsDeterministic = true, IsPrecise = true)] public SqlBoolean STIsEmpty() { - if (this.IsNull) - return SqlBoolean.Null; + if (this.IsNull) + return SqlBoolean.Null; return _geometry.IsEmpty; } diff --git a/src/Microsoft.SqlServer.Types/SqlGeometry.cs b/src/Microsoft.SqlServer.Types/SqlGeometry.cs index 71a508c..01b9d1e 100644 --- a/src/Microsoft.SqlServer.Types/SqlGeometry.cs +++ b/src/Microsoft.SqlServer.Types/SqlGeometry.cs @@ -1,3 +1,5 @@ +using Microsoft.SqlServer.Server; + namespace Microsoft.SqlServer.Types { /// @@ -199,10 +201,10 @@ public SqlInt32 STNumCurves() { if (IsNull || _geometry.IsEmpty) return SqlInt32.Null; - if (_geometry.Type == OGCGeometryType.LineString) - return _geometry.IsEmpty ? 0 : _geometry.NumPoints - 1; + if (_geometry.Type == OGCGeometryType.LineString) + return _geometry.IsEmpty ? 0 : _geometry.NumPoints - 1; - if (_geometry.Type == OGCGeometryType.CircularString) + if (_geometry.Type == OGCGeometryType.CircularString) { if (_geometry.IsEmpty) return 0; return (_geometry.NumPoints - 1) / 2; @@ -211,7 +213,7 @@ public SqlInt32 STNumCurves() return _geometry.NumRings; if (_geometry.Type != OGCGeometryType.CompoundCurve) return SqlInt32.Null; - return _geometry.NumSegments; + return _geometry.NumSegments; } /// diff --git a/src/Microsoft.SqlServer.Types/SqlHierarchyId.cs b/src/Microsoft.SqlServer.Types/SqlHierarchyId.cs index c635782..d27f260 100644 --- a/src/Microsoft.SqlServer.Types/SqlHierarchyId.cs +++ b/src/Microsoft.SqlServer.Types/SqlHierarchyId.cs @@ -1,263 +1,264 @@ -using Microsoft.SqlServer.Types.SqlHierarchy; -using System.Linq; - -namespace Microsoft.SqlServer.Types -{ - /// - /// The SqlHierarchyId type represents a position in a hierarchical structure, specifying depth and breadth. - /// - [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 892, Name = "SqlHierarchyId")] - public struct SqlHierarchyId : IBinarySerialize, INullable, IComparable - { - private HierarchyId _imp; - private bool _isNotNull; - - /// - /// Gets a value indicating whether the is null. - /// - /// Boolean representing true (1) if the node is null; otherwise, false (0). - public bool IsNull => !_isNotNull; // This is a bit backwards, but is done so default(SqlHierarchyId) will return a null id - - /// - /// Gets a with a hierarchy identification of null. - /// - public static SqlHierarchyId Null - { - [SqlMethodAttribute(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, - IsDeterministic = true, IsPrecise = true, IsMutator = false)] - get; - } = new SqlHierarchyId(new HierarchyId(), true); - - private SqlHierarchyId(HierarchyId imp, bool isNull = false) - { - _isNotNull = !isNull; - _imp = imp; - } - - /// - /// Gets a value representing the root node of the hierarchy. - /// - /// A representing the root node of the hierarchical tree. Root value is typically 0x. - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public static SqlHierarchyId GetRoot() => new SqlHierarchyId(HierarchyId.GetRoot()); - - /// - /// Converts the canonical string representation of a node to a value. - /// - /// String representation of node. - /// representing the node described canonically. - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public static SqlHierarchyId Parse(SqlString input) => new SqlHierarchyId(HierarchyId.Parse((string)input)); - - /// - /// Returns a value indicating the results of a comparison between a SqlHierarchyId and an object. - /// - /// An object to be compared to this. - /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: - /// - /// - /// Value - /// Meaning - /// - /// Less than zerothis is less than . - /// Zerothis is equal to . - /// Greater than zerothis is greater than . - /// - /// - /// - /// Throws an exception if is not a node. - /// This member is sealed. - /// - public int CompareTo(object obj) => this.CompareTo((SqlHierarchyId)obj); - - /// - /// Returns a value indicating the results of a comparison between two nodes. - /// - /// A node to compare to this. - /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: - /// - /// - /// Value - /// Meaning - /// - /// Less than zerothis is less than . - /// Zerothis is equal to . - /// Greater than zerothis is greater than . - /// - /// - /// - /// If both nodes are null, returns 0. - /// If one node is null, it is considered to be less than the non-null node. - /// - public int CompareTo(SqlHierarchyId hid) - { - if(IsNull) - { - if (!hid.IsNull) - return -1; - return 0; - } - if (hid.IsNull) - return 1; - if (this < hid) - return -1; - if (this > hid) - return 1; - return 0; - } - - /// - /// Evaluates whether and obj are equal. - /// - /// The object against which to compare this. - /// Boolean. true (1) if this and obj are equal; otherwise, false (0). - /// - /// Returns false (0) if obj is not a SqlHierarchyId node. - /// Returns true (1) if both this and obj are null. - /// - public override bool Equals(object obj) => obj is SqlHierarchyId && Equals((SqlHierarchyId)obj); - - private bool Equals(SqlHierarchyId other) => (IsNull && other.IsNull) || (this == other).IsTrue; - - /// - /// Retrieves the node n levels up the hierarchical tree. - /// - /// An integer representing the number of levels to ascend in the hierarchy. - /// - /// representing the nth ancestor of this. - /// If a number greater than is passed, null is returned. - /// If a negative number is passed, an exception is raised indicating that the argument is out of range. - /// - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public SqlHierarchyId GetAncestor(int n) - { - if (IsNull || _imp.GetLevel() < n) - { - return Null; - } - if (n < 0) - { - throw new ArgumentOutOfRangeException("24011: SqlHierarchyId.GetAncestor failed because 'n' was negative."); - } - return new SqlHierarchyId(_imp.GetAncestor(n)); - } - /// - /// Gets the value of a descendant node that is greater than and less than . - /// - /// The lower bound. - /// The upper bound. - /// A SqlHierarchyId with a value greater than the lower bound and less than the upper bound. - /// - /// - /// If parent is null, returns null. - /// If parent is not null, and both and are null, returns a descendant of parent. - /// If parent and are not null, and is null, returns a descendant of parent greater than . - /// If parent and are not null and is null, returns a descendant of parent less than . - /// If parent, , and child2 are not null, returns a descendant of parent greater than and less than . - /// An exception is raised if or are not null and are not a descendant of parent. - /// If >= , an exception is raised. - /// - /// - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = true, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public SqlHierarchyId GetDescendant(SqlHierarchyId child1, SqlHierarchyId child2) => new SqlHierarchyId(_imp.GetDescendant( - child1.IsNull ? default(HierarchyId?) : child1._imp, - child2.IsNull ? default(HierarchyId?) : child2._imp)); - - /// - /// Gets a hash of the path from the root node of the hierarchy tree to the node. - /// - /// A 32-bit signed integer representing the hash code for this instance. - public override int GetHashCode() => _imp.GetHashCode(); - - /// - /// Gets a value indicating the level of the node in the hierarchical tree. - /// - /// A 16-bit integer indicating the depth of the node in the hierarchical tree. - /// The root of the hierarchy is level 0. - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public SqlInt16 GetLevel() => _imp.GetLevel(); - - /// - /// Gets a value representing the location of a new node that has a path from newRoot equal to the path from oldRoot to this, effectively moving this to the new location. - /// - /// An ancestor of the node specifying the endpoint of the path segment that is to be moved. - /// The node that represents the new ancestor of this. - /// A node representing the new hierarchical location of this. Will return null if , , or this are null. - /// - /// Returns a node whose path from the root is the path to , followed by the path from to this. - /// The data type represents but does not enforce the hierarchical structure. Users must ensure that the node is appropriately structured for the new location. - /// - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public SqlHierarchyId GetReparentedValue(SqlHierarchyId oldRoot, SqlHierarchyId newRoot) - { - if (!IsNull && !oldRoot.IsNull && !newRoot.IsNull) - { - if (!IsDescendantOf(oldRoot)) - { - throw new HierarchyIdException("Instance is not a descendant of 'oldRoot'"); - } - return new SqlHierarchyId(_imp.GetReparentedValue(oldRoot._imp, newRoot._imp)); - } - return Null; - } - /// - /// Gets a value indicating whether the node is the descendant of the parent. - /// - /// The specified node for which the IsDescendantOf test is performed. - /// Boolean, true (1) for all the nodes in the sub-tree rooted at parent; false (0) for all other nodes. - /// parent is considered its own descendant. - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public SqlBoolean IsDescendantOf(SqlHierarchyId parent) => _imp.IsDescendantOf(parent._imp); - - /// - /// Returns the canonical string representation of a node from a value. - /// - /// - /// - /// Called implicitly when a conversion from a data type to a string type occurs. - /// Acts as the opposite of . - /// - /// - /// DECLARE @StringValue AS nvarchar(4000), @hierarchyidValue AS hierarchyid - /// SET @StringValue = '/1/1/3/' - /// SET @hierarchyidValue = 0x5ADE - /// SELECT hierarchyid::Parse(@StringValue) AS hierarchyidRepresentation, - /// @hierarchyidValue.ToString() AS StringRepresentation; - /// GO - /// - [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] - public override string ToString() => _imp.ToString(); - - /// - /// Writes a to a specified binary writer. - /// - /// The specified binary writer. - /// - /// Throws an exception if w is null. - /// Throws an exception if the is null. - /// This member is sealed. - /// - [SqlMethod(IsDeterministic = true, IsPrecise = true)] - public void Write(BinaryWriter w) - { - if (w is null) - throw new ArgumentNullException(nameof(w)); - if (IsNull) - throw new HierarchyIdException("24002: SqlHierarchyId.Write failed because 'this' was a NULL instance."); - BitWriter bw = new BitWriter(w); - - var nodes = this._imp.GetNodes(); - - for (int i = 0; i < nodes.Length; i++) - { - var subNodes = nodes[i]; - for (int j = 0; j < subNodes.Length; j++) - { - bool isLast = j == (subNodes.Length - 1); - - - long val = subNodes[j]; - +using Microsoft.SqlServer.Server; +using Microsoft.SqlServer.Types.SqlHierarchy; +using System.Linq; + +namespace Microsoft.SqlServer.Types +{ + /// + /// The SqlHierarchyId type represents a position in a hierarchical structure, specifying depth and breadth. + /// + [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 892, Name = "SqlHierarchyId")] + public struct SqlHierarchyId : IBinarySerialize, INullable, IComparable + { + private HierarchyId _imp; + private bool _isNotNull; + + /// + /// Gets a value indicating whether the is null. + /// + /// Boolean representing true (1) if the node is null; otherwise, false (0). + public bool IsNull => !_isNotNull; // This is a bit backwards, but is done so default(SqlHierarchyId) will return a null id + + /// + /// Gets a with a hierarchy identification of null. + /// + public static SqlHierarchyId Null + { + [SqlMethodAttribute(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, + IsDeterministic = true, IsPrecise = true, IsMutator = false)] + get; + } = new SqlHierarchyId(new HierarchyId(), true); + + private SqlHierarchyId(HierarchyId imp, bool isNull = false) + { + _isNotNull = !isNull; + _imp = imp; + } + + /// + /// Gets a value representing the root node of the hierarchy. + /// + /// A representing the root node of the hierarchical tree. Root value is typically 0x. + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public static SqlHierarchyId GetRoot() => new SqlHierarchyId(HierarchyId.GetRoot()); + + /// + /// Converts the canonical string representation of a node to a value. + /// + /// String representation of node. + /// representing the node described canonically. + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public static SqlHierarchyId Parse(SqlString input) => new SqlHierarchyId(HierarchyId.Parse((string)input)); + + /// + /// Returns a value indicating the results of a comparison between a SqlHierarchyId and an object. + /// + /// An object to be compared to this. + /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: + /// + /// + /// Value + /// Meaning + /// + /// Less than zerothis is less than . + /// Zerothis is equal to . + /// Greater than zerothis is greater than . + /// + /// + /// + /// Throws an exception if is not a node. + /// This member is sealed. + /// + public int CompareTo(object obj) => this.CompareTo((SqlHierarchyId)obj); + + /// + /// Returns a value indicating the results of a comparison between two nodes. + /// + /// A node to compare to this. + /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: + /// + /// + /// Value + /// Meaning + /// + /// Less than zerothis is less than . + /// Zerothis is equal to . + /// Greater than zerothis is greater than . + /// + /// + /// + /// If both nodes are null, returns 0. + /// If one node is null, it is considered to be less than the non-null node. + /// + public int CompareTo(SqlHierarchyId hid) + { + if(IsNull) + { + if (!hid.IsNull) + return -1; + return 0; + } + if (hid.IsNull) + return 1; + if (this < hid) + return -1; + if (this > hid) + return 1; + return 0; + } + + /// + /// Evaluates whether and obj are equal. + /// + /// The object against which to compare this. + /// Boolean. true (1) if this and obj are equal; otherwise, false (0). + /// + /// Returns false (0) if obj is not a SqlHierarchyId node. + /// Returns true (1) if both this and obj are null. + /// + public override bool Equals(object obj) => obj is SqlHierarchyId && Equals((SqlHierarchyId)obj); + + private bool Equals(SqlHierarchyId other) => (IsNull && other.IsNull) || (this == other).IsTrue; + + /// + /// Retrieves the node n levels up the hierarchical tree. + /// + /// An integer representing the number of levels to ascend in the hierarchy. + /// + /// representing the nth ancestor of this. + /// If a number greater than is passed, null is returned. + /// If a negative number is passed, an exception is raised indicating that the argument is out of range. + /// + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public SqlHierarchyId GetAncestor(int n) + { + if (IsNull || _imp.GetLevel() < n) + { + return Null; + } + if (n < 0) + { + throw new ArgumentOutOfRangeException("24011: SqlHierarchyId.GetAncestor failed because 'n' was negative."); + } + return new SqlHierarchyId(_imp.GetAncestor(n)); + } + /// + /// Gets the value of a descendant node that is greater than and less than . + /// + /// The lower bound. + /// The upper bound. + /// A SqlHierarchyId with a value greater than the lower bound and less than the upper bound. + /// + /// + /// If parent is null, returns null. + /// If parent is not null, and both and are null, returns a descendant of parent. + /// If parent and are not null, and is null, returns a descendant of parent greater than . + /// If parent and are not null and is null, returns a descendant of parent less than . + /// If parent, , and child2 are not null, returns a descendant of parent greater than and less than . + /// An exception is raised if or are not null and are not a descendant of parent. + /// If >= , an exception is raised. + /// + /// + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = true, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public SqlHierarchyId GetDescendant(SqlHierarchyId child1, SqlHierarchyId child2) => new SqlHierarchyId(_imp.GetDescendant( + child1.IsNull ? default(HierarchyId?) : child1._imp, + child2.IsNull ? default(HierarchyId?) : child2._imp)); + + /// + /// Gets a hash of the path from the root node of the hierarchy tree to the node. + /// + /// A 32-bit signed integer representing the hash code for this instance. + public override int GetHashCode() => _imp.GetHashCode(); + + /// + /// Gets a value indicating the level of the node in the hierarchical tree. + /// + /// A 16-bit integer indicating the depth of the node in the hierarchical tree. + /// The root of the hierarchy is level 0. + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public SqlInt16 GetLevel() => _imp.GetLevel(); + + /// + /// Gets a value representing the location of a new node that has a path from newRoot equal to the path from oldRoot to this, effectively moving this to the new location. + /// + /// An ancestor of the node specifying the endpoint of the path segment that is to be moved. + /// The node that represents the new ancestor of this. + /// A node representing the new hierarchical location of this. Will return null if , , or this are null. + /// + /// Returns a node whose path from the root is the path to , followed by the path from to this. + /// The data type represents but does not enforce the hierarchical structure. Users must ensure that the node is appropriately structured for the new location. + /// + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public SqlHierarchyId GetReparentedValue(SqlHierarchyId oldRoot, SqlHierarchyId newRoot) + { + if (!IsNull && !oldRoot.IsNull && !newRoot.IsNull) + { + if (!IsDescendantOf(oldRoot)) + { + throw new HierarchyIdException("Instance is not a descendant of 'oldRoot'"); + } + return new SqlHierarchyId(_imp.GetReparentedValue(oldRoot._imp, newRoot._imp)); + } + return Null; + } + /// + /// Gets a value indicating whether the node is the descendant of the parent. + /// + /// The specified node for which the IsDescendantOf test is performed. + /// Boolean, true (1) for all the nodes in the sub-tree rooted at parent; false (0) for all other nodes. + /// parent is considered its own descendant. + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public SqlBoolean IsDescendantOf(SqlHierarchyId parent) => _imp.IsDescendantOf(parent._imp); + + /// + /// Returns the canonical string representation of a node from a value. + /// + /// + /// + /// Called implicitly when a conversion from a data type to a string type occurs. + /// Acts as the opposite of . + /// + /// + /// DECLARE @StringValue AS nvarchar(4000), @hierarchyidValue AS hierarchyid + /// SET @StringValue = '/1/1/3/' + /// SET @hierarchyidValue = 0x5ADE + /// SELECT hierarchyid::Parse(@StringValue) AS hierarchyidRepresentation, + /// @hierarchyidValue.ToString() AS StringRepresentation; + /// GO + /// + [SqlMethod(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, InvokeIfReceiverIsNull = false, OnNullCall = false, IsDeterministic = true, IsPrecise = true, IsMutator = false)] + public override string ToString() => _imp.ToString(); + + /// + /// Writes a to a specified binary writer. + /// + /// The specified binary writer. + /// + /// Throws an exception if w is null. + /// Throws an exception if the is null. + /// This member is sealed. + /// + [SqlMethod(IsDeterministic = true, IsPrecise = true)] + public void Write(BinaryWriter w) + { + if (w is null) + throw new ArgumentNullException(nameof(w)); + if (IsNull) + throw new HierarchyIdException("24002: SqlHierarchyId.Write failed because 'this' was a NULL instance."); + BitWriter bw = new BitWriter(w); + + var nodes = this._imp.GetNodes(); + + for (int i = 0; i < nodes.Length; i++) + { + var subNodes = nodes[i]; + for (int j = 0; j < subNodes.Length; j++) + { + bool isLast = j == (subNodes.Length - 1); + + + long val = subNodes[j]; + if (isLast) { BitPattern p = KnownPatterns.GetPatternByValue(val); @@ -269,131 +270,131 @@ public void Write(BinaryWriter w) BitPattern p = KnownPatterns.GetPatternByValue(val + 1); ulong value = p.EncodeValue(val + 1) - 1; bw.Write(value, p.BitLength); - } - } - } - - bw.Finish(); - } - - /// - /// Reads from a specified binary reader into a . - /// - /// The specified binary reader. - /// - /// Throws an exception if r is null.
- /// Throws an exception if the SqlHierarchyId is not null.
- /// This member is sealed. - ///
- [SqlMethod(IsDeterministic = true, IsPrecise = true)] - public void Read(BinaryReader r) - { - if (r is null) - throw new ArgumentNullException(nameof(r)); - var bitR = new BitReader(r); - List> result = new List>(); - - while (true) - { - List step = new List(); - - while (true) - { - var p = KnownPatterns.GetPatternByPrefix(bitR); - - if (p == null) - goto finish; - - ulong encodedValue = bitR.Read(p.BitLength); - - long value = p.Decode(encodedValue, out bool isLast); - - step.Add(value); - - if (isLast) - break; - } - - result.Add(step); - } - - finish: - - this._imp = new HierarchyId(result.Select(a => a.ToArray()).ToArray()); - this._isNotNull = !_imp.IsNull; - } - - /// - /// Evaluates whether two nodes are equal. - /// - /// First node to compare. - /// Second node to compare. - /// Boolean. true (1) if and are equal; otherwise, false (0). - /// Returns null if either or are null. - public static SqlBoolean operator ==(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp == hid2._imp; - - /// - /// Evaluates whether two nodes are unequal. - /// - /// First node to compare. - /// Second node to compare. - /// - /// Returns null if either or are null. - public static SqlBoolean operator !=(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp != hid2._imp; - - /// - /// Evaluates whether one specified node is less than another. - /// - /// First node to compare. - /// Second node to compare. - /// - /// Returns null if either or are null. - public static SqlBoolean operator <(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp < hid2._imp; - - /// - /// Evaluates whether one specified node is greater than another. - /// - /// First node to compare. - /// Second node to compare. - /// - /// Returns null if either or are null. - public static SqlBoolean operator >(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp > hid2._imp; - - /// - /// Evaluates whether one specified node is less than or equal to another. - /// - /// First node to compare. - /// Second node to compare. - /// - /// Returns null if either or are null. - public static SqlBoolean operator <=(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp <= hid2._imp; - - /// - /// Evaluates whether one specified node is greater than or equal to another. - /// - /// First node to compare. - /// Second node to compare. - /// - /// Returns null if either or are null. - public static SqlBoolean operator >=(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp >= hid2._imp; - - //public static SqlHierarchyId Deserialize(SqlBytes bytes) - //{ - // using (var r = new BinaryReader(bytes.Stream)) - // { - // var hid = new SqlHierarchyId(new HierarchyId()); - // hid.Read(r); - // return hid; - // } - //} - - //public SqlBytes Serialize() - //{ - // using (var ms = new MemoryStream()) - // { - // Write(new BinaryWriter(ms)); - // return new SqlBytes(ms.ToArray()); - // } - //} - } -} + } + } + } + + bw.Finish(); + } + + /// + /// Reads from a specified binary reader into a . + /// + /// The specified binary reader. + /// + /// Throws an exception if r is null.
+ /// Throws an exception if the SqlHierarchyId is not null.
+ /// This member is sealed. + ///
+ [SqlMethod(IsDeterministic = true, IsPrecise = true)] + public void Read(BinaryReader r) + { + if (r is null) + throw new ArgumentNullException(nameof(r)); + var bitR = new BitReader(r); + List> result = new List>(); + + while (true) + { + List step = new List(); + + while (true) + { + var p = KnownPatterns.GetPatternByPrefix(bitR); + + if (p == null) + goto finish; + + ulong encodedValue = bitR.Read(p.BitLength); + + long value = p.Decode(encodedValue, out bool isLast); + + step.Add(value); + + if (isLast) + break; + } + + result.Add(step); + } + + finish: + + this._imp = new HierarchyId(result.Select(a => a.ToArray()).ToArray()); + this._isNotNull = !_imp.IsNull; + } + + /// + /// Evaluates whether two nodes are equal. + /// + /// First node to compare. + /// Second node to compare. + /// Boolean. true (1) if and are equal; otherwise, false (0). + /// Returns null if either or are null. + public static SqlBoolean operator ==(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp == hid2._imp; + + /// + /// Evaluates whether two nodes are unequal. + /// + /// First node to compare. + /// Second node to compare. + /// + /// Returns null if either or are null. + public static SqlBoolean operator !=(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp != hid2._imp; + + /// + /// Evaluates whether one specified node is less than another. + /// + /// First node to compare. + /// Second node to compare. + /// + /// Returns null if either or are null. + public static SqlBoolean operator <(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp < hid2._imp; + + /// + /// Evaluates whether one specified node is greater than another. + /// + /// First node to compare. + /// Second node to compare. + /// + /// Returns null if either or are null. + public static SqlBoolean operator >(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp > hid2._imp; + + /// + /// Evaluates whether one specified node is less than or equal to another. + /// + /// First node to compare. + /// Second node to compare. + /// + /// Returns null if either or are null. + public static SqlBoolean operator <=(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp <= hid2._imp; + + /// + /// Evaluates whether one specified node is greater than or equal to another. + /// + /// First node to compare. + /// Second node to compare. + /// + /// Returns null if either or are null. + public static SqlBoolean operator >=(SqlHierarchyId hid1, SqlHierarchyId hid2) => hid1.IsNull || hid2.IsNull ? SqlBoolean.Null : hid1._imp >= hid2._imp; + + //public static SqlHierarchyId Deserialize(SqlBytes bytes) + //{ + // using (var r = new BinaryReader(bytes.Stream)) + // { + // var hid = new SqlHierarchyId(new HierarchyId()); + // hid.Read(r); + // return hid; + // } + //} + + //public SqlBytes Serialize() + //{ + // using (var ms = new MemoryStream()) + // { + // Write(new BinaryWriter(ms)); + // return new SqlBytes(ms.ToArray()); + // } + //} + } +}