Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding SqlJson and Data write with SqlJson #2880

Merged
merged 4 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
28 changes: 28 additions & 0 deletions doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<docs>
<members name="SqlJson">
<SqlJson>
<summary>Represents the JSON datatype in SQL Server.</summary>
</SqlJson>
<ctor1>
<summary>Parameterless constructor. Initializes a new instance of the SqlJson class which represents a null JSON value.</summary>
</ctor1>
<ctor2>
<param name="jsonString"></param>
<summary>Takes a <see cref="string"/> as input and initializes a new instance of the SqlJson class.</summary>
</ctor2>
<ctor3>
<param name="jsonDoc"></param>
<summary>Takes a <see cref="System.Text.Json.JsonDocument"/> as input and initializes a new instance of the SqlJson class.</summary>
</ctor3>
<IsNull>
<inheritdoc/>
</IsNull>
<Null>
<summary>Represents a null instance of the <see cref="SqlJson"/> type.</summary>
</Null>
<Value>
<summary>Gets the string representation of the Json content of this <see cref="SqlJson" /> instance.</summary>
</Value>
</members>
</docs>
1 change: 1 addition & 0 deletions src/Microsoft.Data.SqlClient.sln
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlTypes", "Microsoft.Data.SqlTypes", "{5A7600BD-AED8-44AB-8F2A-7CB33A8D9C02}"
ProjectSection(SolutionItems) = preProject
..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml
..\doc\snippets\Microsoft.Data.SqlTypes\SqlJson.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlJson.xml
EndProjectSection
EndProject
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ public override void EndWrite(System.IAsyncResult asyncResult) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml' path='docs/members[@name="SqlFileStream"]/WriteByte/*' />
public override void WriteByte(byte value) { }
}

/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/SqlJson/*' />
public class SqlJson : System.Data.SqlTypes.INullable
{
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor1/*' />
public SqlJson() { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor2/*' />
public SqlJson(string jsonString) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor3/*' />
public SqlJson(System.Text.Json.JsonDocument jsonDoc) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/IsNull/*' />
public bool IsNull => throw null;
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Null/*' />
public static SqlJson Null => throw null;
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Value/*' />
public string Value { get { throw null; } }
}
}
namespace Microsoft.Data.SqlClient
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,9 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs">
<Link>Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlJson.cs">
<Link>Microsoft\Data\SqlTypes\SqlJson.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Resources\ResCategoryAttribute.cs">
<Link>Resources\ResCategoryAttribute.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
Expand Down Expand Up @@ -2541,6 +2542,18 @@ virtual public SqlXml GetSqlXml(int i)
return sx;
}

/// <summary>
/// Retrieves the column at ordinal as a SqlJson.
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
virtual public SqlJson GetSqlJson(int i)
deepaksa1 marked this conversation as resolved.
Show resolved Hide resolved
{
ReadColumn(i);
SqlJson json = _data[i].IsNull ? SqlJson.Null : _data[i].SqlJson;
return json;
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/GetSqlValue/*' />
virtual public object GetSqlValue(int i)
{
Expand Down Expand Up @@ -2994,6 +3007,16 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
return (T)(object)new MemoryStream(value, writable: false);
}
}
else if (typeof(T) == typeof(JsonDocument))
{
MetaType metaType = metaData.metaType;
if (metaType.SqlDbType != SqlDbTypeExtensions.Json)
{
throw SQL.JsonDocumentNotSupportedOnColumnType(metaData.column);
}
JsonDocument document = JsonDocument.Parse(data.Value as string);
return (T)(object)document;
}
else
{
if (typeof(INullable).IsAssignableFrom(typeof(T)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5952,7 +5952,6 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
case TdsEnums.SQLVARCHAR:
case TdsEnums.SQLBIGVARCHAR:
case TdsEnums.SQLTEXT:
case TdsEnums.SQLJSON:
// If bigvarchar(max), we only read the first chunk here,
// expecting the caller to read the rest
if (encoding == null)
Expand All @@ -5970,6 +5969,17 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
value.SetToString(stringValue);
break;

case TdsEnums.SQLJSON:
encoding = Encoding.UTF8;
string jsonStringValue;
result = stateObj.TryReadStringWithEncoding(length, encoding, isPlp, out jsonStringValue);
if (result != TdsOperationStatus.Done)
{
return result;
}
value.SetToJson(jsonStringValue);
break;

case TdsEnums.SQLNCHAR:
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
Expand Down Expand Up @@ -9616,7 +9626,8 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
mt.TDSType != TdsEnums.SQLXMLTYPE &&
mt.TDSType != TdsEnums.SQLIMAGE &&
mt.TDSType != TdsEnums.SQLTEXT &&
mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");
mt.TDSType != TdsEnums.SQLNTEXT &&
mt.TDSType != TdsEnums.SQLJSON, "Type unsupported for encryption");

byte[] serializedValue = null;
byte[] encryptedValue = null;
Expand Down
17 changes: 17 additions & 0 deletions src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2400,4 +2400,21 @@ public override void EndWrite(System.IAsyncResult asyncResult) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml' path='docs/members[@name="SqlFileStream"]/WriteByte/*' />
public override void WriteByte(byte value) { }
}

/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/SqlJson/*' />
public class SqlJson : System.Data.SqlTypes.INullable
{
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor1/*' />
public SqlJson() { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor2/*' />
public SqlJson(string jsonString) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor3/*' />
public SqlJson(System.Text.Json.JsonDocument jsonDoc) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/IsNull/*' />
public bool IsNull => throw null;
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Null/*' />
public static SqlJson Null => throw null;
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Value/*' />
public string Value { get { throw null; } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,9 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs">
<Link>Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlJson.cs">
<Link>Microsoft\Data\SqlTypes\SqlJson.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Resources\ResCategoryAttribute.cs">
<Link>Resources\ResCategoryAttribute.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
Expand Down Expand Up @@ -2894,6 +2895,19 @@ virtual public SqlXml GetSqlXml(int i)
return sx;
}

/// <summary>
/// Retrieves the column at ordinal as a SqlJson.
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
virtual public SqlJson GetSqlJson(int i)
deepaksa1 marked this conversation as resolved.
Show resolved Hide resolved
{
ReadColumn(i);
SqlJson json = _data[i].IsNull ? SqlJson.Null : _data[i].SqlJson;

return json;
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/GetSqlValue/*' />
virtual public object GetSqlValue(int i)
{
Expand Down Expand Up @@ -3341,6 +3355,16 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
return (T)(object)new MemoryStream(value, writable: false);
}
}
else if (typeof(T) == typeof(JsonDocument))
{
MetaType metaType = metaData.metaType;
if (metaType.SqlDbType != SqlDbTypeExtensions.Json)
{
throw SQL.JsonDocumentNotSupportedOnColumnType(metaData.column);
}
JsonDocument document = JsonDocument.Parse(data.Value as string);
return (T)(object)document;
}
else
{
if (typeof(INullable).IsAssignableFrom(typeof(T)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6801,7 +6801,6 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
case TdsEnums.SQLVARCHAR:
case TdsEnums.SQLBIGVARCHAR:
case TdsEnums.SQLTEXT:
case TdsEnums.SQLJSON:
// If bigvarchar(max), we only read the first chunk here,
// expecting the caller to read the rest
if (encoding == null)
Expand All @@ -6818,6 +6817,16 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
}
value.SetToString(stringValue);
break;
case TdsEnums.SQLJSON:
encoding = Encoding.UTF8;
string jsonStringValue;
result = stateObj.TryReadStringWithEncoding(length, encoding, isPlp, out jsonStringValue);
if (result != TdsOperationStatus.Done)
{
return result;
}
value.SetToJson(jsonStringValue);
break;

case TdsEnums.SQLNCHAR:
case TdsEnums.SQLNVARCHAR:
Expand Down Expand Up @@ -10370,7 +10379,8 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout
mt.TDSType != TdsEnums.SQLXMLTYPE &&
mt.TDSType != TdsEnums.SQLIMAGE &&
mt.TDSType != TdsEnums.SQLTEXT &&
mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");
mt.TDSType != TdsEnums.SQLNTEXT &&
mt.TDSType != TdsEnums.SQLJSON, "Type unsupported for encryption");

byte[] serializedValue = null;
byte[] encryptedValue = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal enum StorageType
DateTime2,
DateTimeOffset,
Time,
Json,
}

internal struct DateTimeInfo
Expand Down Expand Up @@ -486,7 +487,7 @@ internal string String
{
ThrowIfNull();

if (StorageType.String == _type)
if (StorageType.String == _type || StorageType.Json == _type)
{
return (string)_object;
}
Expand Down Expand Up @@ -916,7 +917,8 @@ internal SqlString SqlString
{
get
{
if (StorageType.String == _type)
// String and Json storage type are both strings.
if (StorageType.String == _type || StorageType.Json == _type)
{
if (IsNull)
{
Expand All @@ -937,6 +939,22 @@ internal SqlString SqlString
}
}

internal SqlJson SqlJson
{
get
{
if (StorageType.Json == _type)
{
if (IsNull)
{
return SqlTypes.SqlJson.Null;
}
return new SqlJson((string)_object);
}
return (SqlJson)SqlValue;
}
}
deepaksa1 marked this conversation as resolved.
Show resolved Hide resolved

internal object SqlValue
{
get
Expand Down Expand Up @@ -969,7 +987,8 @@ internal object SqlValue
return SqlSingle;
case StorageType.String:
return SqlString;

case StorageType.Json:
return SqlJson;
case StorageType.SqlCachedBuffer:
{
SqlCachedBuffer data = (SqlCachedBuffer)(_object);
Expand Down Expand Up @@ -1087,6 +1106,8 @@ internal object Value
return DateTimeOffset;
case StorageType.Time:
return Time;
case StorageType.Json:
return String;
}
return null; // need to return the value as an object of some CLS type
}
Expand Down Expand Up @@ -1132,6 +1153,8 @@ internal Type GetTypeFromStorageType(bool isSqlType)
return typeof(SqlGuid);
case StorageType.SqlXml:
return typeof(SqlXml);
case StorageType.Json:
return typeof(SqlJson);
// Time Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
}
}
Expand Down Expand Up @@ -1179,6 +1202,8 @@ internal Type GetTypeFromStorageType(bool isSqlType)
return typeof(DateTime);
case StorageType.DateTimeOffset:
return typeof(DateTimeOffset);
case StorageType.Json:
return typeof(string);
#if NET6_0_OR_GREATER
case StorageType.Time:
return typeof(TimeOnly);
Expand Down Expand Up @@ -1274,6 +1299,14 @@ internal void SetToString(string value)
_isNull = false;
}

internal void SetToJson(string value)
{
Debug.Assert(IsEmpty, "setting value a second time?");
_object = value;
_type = StorageType.Json;
_isNull = false;
}

internal void SetToDate(ReadOnlySpan<byte> bytes)
{
Debug.Assert(IsEmpty, "setting value a second time?");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System.IO;
using System.Xml;
using Microsoft.Data.Common;
using Microsoft.Data.SqlTypes;
using Microsoft.SqlServer.Server;

namespace Microsoft.Data.SqlClient
Expand Down Expand Up @@ -364,6 +365,8 @@ private static MetaType GetMetaTypeFromValue(Type dataType, object value, bool i
return s_metaReal;
else if (dataType == typeof(SqlXml))
return MetaXml;
else if (dataType == typeof(SqlJson))
return s_MetaJson;
else if (dataType == typeof(SqlString))
{
return ((inferLen && !((SqlString)value).IsNull)
Expand Down
Loading
Loading