Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# JetBrains Rider (cross platform .NET IDE) working folder
.idea/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1573,7 +1573,7 @@ override public Stream GetStream(int i)
else
{
// Grab already read data
data = _data[i].SqlBinary.Value;
data = _data[i].ByteArray;
}

// If non-sequential then we just have a read-only MemoryStream
Expand Down Expand Up @@ -2969,7 +2969,7 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
}
else
{
byte[] value = data.IsNull ? Array.Empty<byte>() : data.SqlBinary.Value;
byte[] value = data.IsNull ? Array.Empty<byte>() : data.ByteArray;
return (T)(object)new MemoryStream(value, writable: false);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5500,11 +5500,11 @@ internal static object GetNullSqlValue(SqlBuffer nullVal, SqlMetaDataPriv md, Sq
case SqlDbType.Binary:
case SqlDbType.VarBinary:
case SqlDbType.Image:
nullVal.SqlBinary = SqlBinary.Null;
nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlBinary);
break;

case SqlDbType.UniqueIdentifier:
nullVal.SqlGuid = SqlGuid.Null;
nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlGuid);
break;

case SqlDbType.Bit:
Expand Down Expand Up @@ -5932,7 +5932,11 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
case TdsEnums.SQLUNIQUEID:
{
Debug.Assert(length == 16, "invalid length for SqlGuid type!");
#if NET8_0_OR_GREATER
value.SqlGuid = new SqlGuid(unencryptedBytes); // doesn't copy the byte array
#else
value.SqlGuid = SqlTypeWorkarounds.SqlGuidCtor(unencryptedBytes, true); // doesn't copy the byte array
#endif
break;
}

Expand All @@ -5953,7 +5957,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
unencryptedBytes = bytes;
}

value.SqlBinary = new SqlBinary(unencryptedBytes); // doesn't copy the byte array
value.ByteArray = unencryptedBytes; // doesn't copy the byte array
break;
}

Expand Down Expand Up @@ -6146,11 +6150,7 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T
}
else
{
#if NET7_0_OR_GREATER
value.SqlBinary = SqlBinary.WrapBytes(b);
#else
value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); // doesn't copy the byte array
#endif
value.ByteArray = b; // doesn't copy the byte array
}
break;

Expand Down Expand Up @@ -6443,12 +6443,8 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length,
{
return false;
}
#if NET7_0_OR_GREATER
value.SqlBinary = SqlBinary.WrapBytes(b);
#else
value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true);
#endif

value.ByteArray = b; // doesn't copy byte array
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,31 @@ private struct SqlBinaryCaster
internal SqlBinaryLookalike Fake;
}
#endregion

#region Work around inability to access SqlGuid.ctor(byte[], bool)
internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored)
{
// Construct a SqlGuid without allocating/copying the byte[]. This provides
// the same behavior as SqlGuid.ctor(byte[], bool).
var c = default(SqlGuidCaster);
c.Fake._value = value;
return c.Real;
}

[StructLayout(LayoutKind.Sequential)]
private struct SqlGuidLookalike
{
internal byte[] _value;
}

[StructLayout(LayoutKind.Explicit)]
private struct SqlGuidCaster
{
[FieldOffset(0)]
internal SqlGuid Real;
[FieldOffset(0)]
internal SqlGuidLookalike Fake;
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1720,7 +1720,7 @@ virtual public XmlReader GetXmlReader(int i)
if (_data[i].IsNull)
{
// A 'null' stream
return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(new byte[0], writable: false), closeInput: true, async: false);
return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(Array.Empty<byte>(), writable: false), closeInput: true, async: false);
}
else
{
Expand Down Expand Up @@ -1765,12 +1765,12 @@ override public Stream GetStream(int i)
if (_data[i].IsNull)
{
// A 'null' stream
data = new byte[0];
data = Array.Empty<byte>();
}
else
{
// Grab already read data
data = _data[i].SqlBinary.Value;
data = _data[i].ByteArray;
}

// If non-sequential then we just have a read-only MemoryStream
Expand Down Expand Up @@ -3308,7 +3308,7 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
}
else
{
byte[] value = data.IsNull ? Array.Empty<byte>() : data.SqlBinary.Value;
byte[] value = data.IsNull ? Array.Empty<byte>() : data.ByteArray;
return (T)(object)new MemoryStream(value, writable: false);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6306,11 +6306,11 @@ internal static object GetNullSqlValue(
case SqlDbType.Binary:
case SqlDbType.VarBinary:
case SqlDbType.Image:
nullVal.SqlBinary = SqlBinary.Null;
nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlBinary);
break;

case SqlDbType.UniqueIdentifier:
nullVal.SqlGuid = SqlGuid.Null;
nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlGuid);
break;

case SqlDbType.Bit:
Expand Down Expand Up @@ -6741,7 +6741,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
unencryptedBytes = bytes;
}

value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(unencryptedBytes, true); // doesn't copy the byte array
value.ByteArray = unencryptedBytes; // doesn't copy the byte array
break;
}

Expand Down Expand Up @@ -6938,7 +6938,7 @@ internal bool TryReadSqlValue(SqlBuffer value,
}
else
{
value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); // doesn't copy the byte array
value.ByteArray = b; // doesn't copy the byte array
}
break;

Expand Down Expand Up @@ -7238,8 +7238,8 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length,
{
return false;
}
value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); // doesn't copy the byte array

value.ByteArray = b; // doesn't copy the byte array
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal static Stream GetStream(SmiEventSink_Default sink, ITypedGettersV3 gett
if (isDbNull)
{
// "null" stream
data = new byte[0];
data = Array.Empty<byte>();
}
else
{
Expand Down Expand Up @@ -147,15 +147,6 @@ private static void GetNullOutputParameterSmi(SmiMetaData metaData, SqlBuffer ta
{
result = DBNull.Value;
}
else if (SqlBuffer.StorageType.SqlBinary == stype)
{
// special case SqlBinary, 'cause tds parser never sets SqlBuffer to null, just to empty!
targetBuffer.SqlBinary = SqlBinary.Null;
}
else if (SqlBuffer.StorageType.SqlGuid == stype)
{
targetBuffer.SqlGuid = SqlGuid.Null;
}
else
{
targetBuffer.SetToNullOfType(stype);
Expand Down Expand Up @@ -190,7 +181,7 @@ SqlBuffer targetBuffer // destination
case SqlDbType.Image:
case SqlDbType.Timestamp:
case SqlDbType.VarBinary:
targetBuffer.SqlBinary = GetSqlBinary_Unchecked(sink, getters, ordinal);
targetBuffer.ByteArray = GetByteArray_Unchecked(sink, getters, ordinal);
break;
case SqlDbType.Bit:
targetBuffer.Boolean = GetBoolean_Unchecked(sink, getters, ordinal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ internal struct Storage
private bool _isNull;
private StorageType _type;
private Storage _value;
private object _object; // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml
private object _object; // String, byte[] for SqlBinary, SqlCachedBuffer, SqlString, SqlXml

internal SqlBuffer()
{
Expand Down Expand Up @@ -179,7 +179,19 @@ internal byte[] ByteArray
get
{
ThrowIfNull();
return SqlBinary.Value;

if (StorageType.SqlBinary == _type)
{
return (byte[])_object;
}
return ((SqlBinary)SqlValue).Value; // anything else we haven't thought of goes through boxing.
}
set
{
Debug.Assert(IsEmpty, "setting value a second time?");
_object = value;
_type = StorageType.SqlBinary;
_isNull = value is null;
}
}

Expand Down Expand Up @@ -653,22 +665,16 @@ internal SqlBinary SqlBinary
{
get
{
if (StorageType.SqlBinary == _type)
if (IsNull)
{
if (IsNull)
{
return SqlBinary.Null;
}
return (SqlBinary)_object;
return SqlBinary.Null;
}
return (SqlBinary)SqlValue; // anything else we haven't thought of goes through boxing.
}
set
{
Debug.Assert(IsEmpty, "setting value a second time?");
_object = value;
_type = StorageType.SqlBinary;
_isNull = value.IsNull;

#if NET7_0_OR_GREATER
return SqlBinary.WrapBytes(ByteArray);
#else
return SqlTypeWorkarounds.SqlBinaryCtor(ByteArray, true); // doesn't copy the byte array
#endif
}
}

Expand Down Expand Up @@ -815,14 +821,17 @@ internal SqlGuid SqlGuid
}
else if (StorageType.SqlGuid == _type)
{
return IsNull ? SqlGuid.Null : (SqlGuid)_object;
return IsNull ? SqlGuid.Null : new SqlGuid(_value._guid);
}
return (SqlGuid)SqlValue; // anything else we haven't thought of goes through boxing.
}
set
{
Debug.Assert(IsEmpty, "setting value a second time?");
_object = value;
if (!value.IsNull)
{
_value._guid = value.Value;
}
_type = StorageType.SqlGuid;
_isNull = value.IsNull;
}
Expand Down