diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 2cd3537ea7..ec38fe2f3b 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -919,6 +919,7 @@
True
Strings.resx
+
Resources\Strings.resx
Microsoft.Data.SqlClient.Resources.Strings.resources
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs
index 94325d3c88..adb48ad94c 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs
@@ -6464,7 +6464,7 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete
paramList.Append(size);
paramList.Append(')');
}
- else if (mt.IsPlp && (mt.SqlDbType != SqlDbType.Xml) && (mt.SqlDbType != SqlDbType.Udt))
+ else if (mt.IsPlp && (mt.SqlDbType != SqlDbType.Xml) && (mt.SqlDbType != SqlDbType.Udt) && (mt.SqlDbType != (SqlDbType)35))
{
paramList.Append("(max) ");
}
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
index 5ef3d781ea..5050d9ea73 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
@@ -1365,6 +1365,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword,
// The SQLDNSCaching feature is implicitly set
requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching;
+ requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport;
+
_parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt);
}
@@ -2812,6 +2814,24 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
break;
}
+ case TdsEnums.FEATUREEXT_JSONSUPPORT:
+ {
+ SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for JSONSUPPORT", ObjectID);
+ if (data.Length != 1)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown token for JSONSUPPORT", ObjectID);
+ throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream);
+ }
+ byte JsonSupportVersion = data[0];
+ if (JsonSupportVersion == 0 || JsonSupportVersion > 1)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(" {0}, Invalid version number for JSONSUPPORT", ObjectID);
+ throw SQL.ParsingError();
+ }
+ _parser.IsJsonSupportEnabled = true;
+ break;
+ }
+
default:
{
// Unknown feature ack
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
index abb16eaebe..afb0aa05e9 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -194,6 +194,8 @@ internal static void Assert(string message)
///
internal int DataClassificationVersion { get; set; }
+ internal bool IsJsonSupportEnabled = false;
+
private SqlCollation _cachedCollation;
internal TdsParser(bool MARS, bool fAsynchronous)
@@ -5703,6 +5705,7 @@ private bool TryReadSqlStringValue(SqlBuffer value, byte type, int length, Encod
case TdsEnums.SQLVARCHAR:
case TdsEnums.SQLBIGVARCHAR:
case TdsEnums.SQLTEXT:
+ case TdsEnums.SQLJSONTYPE:
// If bigvarchar(max), we only read the first chunk here,
// expecting the caller to read the rest
if (encoding == null)
@@ -6160,6 +6163,7 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T
case TdsEnums.SQLNCHAR:
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
+ case TdsEnums.SQLJSONTYPE:
if (!TryReadSqlStringValue(value, tdsType, length, md.encoding, isPlp, stateObj))
{
return false;
@@ -7650,6 +7654,7 @@ internal bool TryGetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject sta
if (colmeta.metaType.IsPlp)
{
Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
+ colmeta.tdsType == TdsEnums.SQLJSONTYPE ||
colmeta.tdsType == TdsEnums.SQLBIGVARCHAR ||
colmeta.tdsType == TdsEnums.SQLBIGVARBINARY ||
colmeta.tdsType == TdsEnums.SQLNVARCHAR ||
@@ -8106,6 +8111,21 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD
return len;
}
+ internal int WriteJsonSupportFeatureRequest(bool write /* if false just calculates the length */)
+ {
+ int len = 6; // 1byte = featureID, 4bytes = featureData length, 1 bytes = Version
+
+ if (write)
+ {
+ // Write Feature ID
+ _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_JSONSUPPORT);
+ WriteInt(1, _physicalStateObj);
+ _physicalStateObj.WriteByte(1);
+ }
+
+ return len;
+ }
+
private void WriteLoginData(SqlLogin rec,
TdsEnums.FeatureExtension requestedFeatures,
SessionData recoverySessionData,
@@ -8416,6 +8436,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
length += WriteSQLDNSCachingFeatureRequest(write);
}
+ if ((requestedFeatures & TdsEnums.FeatureExtension.JsonSupport) != 0)
+ {
+ length += WriteJsonSupportFeatureRequest(write);
+ }
+
length++; // for terminator
if (write)
{
@@ -11153,6 +11178,7 @@ private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLe
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
case TdsEnums.SQLXMLTYPE:
+ case TdsEnums.SQLJSONTYPE:
if (type.IsPlp)
{
@@ -11803,6 +11829,7 @@ private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
case TdsEnums.SQLXMLTYPE:
+ case TdsEnums.SQLJSONTYPE:
{
Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader");
Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string");
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlJson.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlJson.cs
new file mode 100644
index 0000000000..4c90145aac
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlJson.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Data.SqlClient
+{
+ internal class SqlJson
+ {
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index e291206a4a..f9e8162081 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -695,6 +695,7 @@
+
@@ -764,6 +765,9 @@
$(SystemBuffersVersion)
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs
index 94cd927c1d..87f8e1cc31 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs
@@ -7172,7 +7172,7 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete
paramList.Append(size);
paramList.Append(')');
}
- else if (mt.IsPlp && (mt.SqlDbType != SqlDbType.Xml) && (mt.SqlDbType != SqlDbType.Udt))
+ else if (mt.IsPlp && (mt.SqlDbType != SqlDbType.Xml) && (mt.SqlDbType != SqlDbType.Udt) && (mt.SqlDbType != (SqlDbType)35))
{
paramList.Append("(max) ");
}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
index 612aab532e..328009b1a5 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
@@ -1638,6 +1638,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword,
// The SQLDNSCaching feature is implicitly set
requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching;
+ requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport;
+
_parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, _originalNetworkAddressInfo, encrypt);
}
@@ -3239,6 +3241,24 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
break;
}
+ case TdsEnums.FEATUREEXT_JSONSUPPORT:
+ {
+ SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received feature extension acknowledgement for JSONSUPPORT", ObjectID);
+ if (data.Length != 1)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(" {0}, Unknown token for JSONSUPPORT", ObjectID);
+ throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream);
+ }
+ byte JsonSupportVersion = data[0];
+ if (JsonSupportVersion == 0 || JsonSupportVersion > 1)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(" {0}, Invalid version number for JSONSUPPORT", ObjectID);
+ throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream);
+ }
+ _parser.IsJsonSupportEnabled = true;
+ break;
+ }
+
default:
{
// Unknown feature ack
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
index 7c8fc724c6..20a35618ac 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -25,6 +25,7 @@
using Microsoft.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using Microsoft.Data.ProviderBase;
+using System.IdentityModel.Tokens;
namespace Microsoft.Data.SqlClient
{
@@ -311,6 +312,8 @@ internal bool IsDataClassificationEnabled
///
internal int DataClassificationVersion { get; set; }
+ internal bool IsJsonSupportEnabled = false;
+
private SqlCollation _cachedCollation;
internal TdsParser(bool MARS, bool fAsynchronous)
@@ -6513,6 +6516,7 @@ private bool TryReadSqlStringValue(SqlBuffer value, byte type, int length, Encod
case TdsEnums.SQLVARCHAR:
case TdsEnums.SQLBIGVARCHAR:
case TdsEnums.SQLTEXT:
+ case TdsEnums.SQLJSONTYPE:
// If bigvarchar(max), we only read the first chunk here,
// expecting the caller to read the rest
if (encoding == null)
@@ -6951,6 +6955,7 @@ internal bool TryReadSqlValue(SqlBuffer value,
case TdsEnums.SQLNCHAR:
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
+ case TdsEnums.SQLJSONTYPE:
if (!TryReadSqlStringValue(value, tdsType, length, md.encoding, isPlp, stateObj))
{
return false;
@@ -8426,6 +8431,7 @@ internal bool TryGetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject sta
if (_is2005 && colmeta.metaType.IsPlp)
{
Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
+ colmeta.tdsType == TdsEnums.SQLJSONTYPE ||
colmeta.tdsType == TdsEnums.SQLBIGVARCHAR ||
colmeta.tdsType == TdsEnums.SQLBIGVARBINARY ||
colmeta.tdsType == TdsEnums.SQLNVARCHAR ||
@@ -8905,6 +8911,21 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD
return len;
}
+ internal int WriteJsonSupportFeatureRequest(bool write /* if false just calculates the length */)
+ {
+ int len = 6; // 1byte = featureID, 4bytes = featureData length, 1 bytes = Version
+
+ if (write)
+ {
+ // Write Feature ID
+ _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_JSONSUPPORT);
+ WriteInt(1, _physicalStateObj);
+ _physicalStateObj.WriteByte(1);
+ }
+
+ return len;
+ }
+
private void WriteLoginData(SqlLogin rec,
TdsEnums.FeatureExtension requestedFeatures,
SessionData recoverySessionData,
@@ -9232,6 +9253,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
length += WriteSQLDNSCachingFeatureRequest(write);
}
+ if ((requestedFeatures & TdsEnums.FeatureExtension.JsonSupport) != 0)
+ {
+ length += WriteJsonSupportFeatureRequest(write);
+ }
+
length++; // for terminator
if (write)
{
@@ -10201,7 +10227,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout
}
else if (mt.IsPlp)
{
- if (mt.SqlDbType != SqlDbType.Xml)
+ if (mt.SqlDbType != SqlDbType.Xml && mt.SqlDbType != (SqlDbType)35)
WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
}
else if ((!mt.IsVarTime) && (mt.SqlDbType != SqlDbType.Date))
@@ -10242,50 +10268,53 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout
// write out collation or xml metadata
- if (_is2005 && (mt.SqlDbType == SqlDbType.Xml))
+ if ((mt.SqlDbType == SqlDbType.Xml || mt.SqlDbType == (SqlDbType)35))
{
- if (!string.IsNullOrEmpty(param.XmlSchemaCollectionDatabase) ||
- !string.IsNullOrEmpty(param.XmlSchemaCollectionOwningSchema) ||
- !string.IsNullOrEmpty(param.XmlSchemaCollectionName))
+ if (mt.SqlDbType == SqlDbType.Xml)
{
- stateObj.WriteByte(1); //Schema present flag
-
- if (!string.IsNullOrEmpty(param.XmlSchemaCollectionDatabase))
+ if (!string.IsNullOrEmpty(param.XmlSchemaCollectionDatabase) ||
+ !string.IsNullOrEmpty(param.XmlSchemaCollectionOwningSchema) ||
+ !string.IsNullOrEmpty(param.XmlSchemaCollectionName))
{
- tempLen = (param.XmlSchemaCollectionDatabase).Length;
- stateObj.WriteByte((byte)(tempLen));
- WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
- }
- else
- {
- stateObj.WriteByte(0); // No dbname
- }
+ stateObj.WriteByte(1); //Schema present flag
+
+ if (!string.IsNullOrEmpty(param.XmlSchemaCollectionDatabase))
+ {
+ tempLen = (param.XmlSchemaCollectionDatabase).Length;
+ stateObj.WriteByte((byte)(tempLen));
+ WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
+ }
+ else
+ {
+ stateObj.WriteByte(0); // No dbname
+ }
+
+ if (!string.IsNullOrEmpty(param.XmlSchemaCollectionOwningSchema))
+ {
+ tempLen = (param.XmlSchemaCollectionOwningSchema).Length;
+ stateObj.WriteByte((byte)(tempLen));
+ WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
+ }
+ else
+ {
+ stateObj.WriteByte(0); // no xml schema name
+ }
+ if (!string.IsNullOrEmpty(param.XmlSchemaCollectionName))
+ {
+ tempLen = (param.XmlSchemaCollectionName).Length;
+ WriteShort((short)(tempLen), stateObj);
+ WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj);
+ }
+ else
+ {
+ WriteShort(0, stateObj); // No xml schema collection name
+ }
- if (!string.IsNullOrEmpty(param.XmlSchemaCollectionOwningSchema))
- {
- tempLen = (param.XmlSchemaCollectionOwningSchema).Length;
- stateObj.WriteByte((byte)(tempLen));
- WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
- }
- else
- {
- stateObj.WriteByte(0); // no xml schema name
- }
- if (!string.IsNullOrEmpty(param.XmlSchemaCollectionName))
- {
- tempLen = (param.XmlSchemaCollectionName).Length;
- WriteShort((short)(tempLen), stateObj);
- WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj);
}
else
{
- WriteShort(0, stateObj); // No xml schema collection name
+ stateObj.WriteByte(0); // No schema
}
-
- }
- else
- {
- stateObj.WriteByte(0); // No schema
}
}
else if (_is2000 && mt.IsCharType)
@@ -11832,6 +11861,10 @@ private void WriteTokenLength(byte token, int length, TdsParserStateObject state
{
tokenLength = 8;
}
+ else if (token == TdsEnums.SQLJSONTYPE)
+ {
+ tokenLength = 8;
+ }
}
if (tokenLength == 0)
@@ -12063,10 +12096,19 @@ private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLe
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
case TdsEnums.SQLXMLTYPE:
+ case TdsEnums.SQLJSONTYPE:
if (type.IsPlp)
{
- if (IsBOMNeeded(type, value))
+ if (type.NullableType == TdsEnums.SQLJSONTYPE)
+ {
+ //string s = value.ToString();
+ // TODO : Performance and BOM check. Saurabh
+ byte[] jsonAsBytes = Encoding.UTF8.GetBytes(value.ToString());
+ WriteInt(jsonAsBytes.Length, stateObj);
+ return stateObj.WriteByteArray(jsonAsBytes, jsonAsBytes.Length, 0, canAccumulate: false);
+ }
+ else if (IsBOMNeeded(type, value))
{
WriteInt(actualLength + 2, stateObj); // chunk length
WriteShort(TdsEnums.XMLUNICODEBOM, stateObj);
@@ -12091,7 +12133,6 @@ private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLe
Debug.Assert(value is SqlString);
return WriteString(((SqlString)value).Value, actualLength, offset, stateObj, canAccumulate: false);
}
-
case TdsEnums.SQLNUMERICN:
Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
WriteSqlDecimal((SqlDecimal)value, stateObj);
@@ -12755,6 +12796,7 @@ private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
case TdsEnums.SQLXMLTYPE:
+ case TdsEnums.SQLJSONTYPE:
{
Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader");
Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string");
@@ -12776,7 +12818,14 @@ private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int
{
if (type.IsPlp)
{
- if (IsBOMNeeded(type, value))
+ if (type.NullableType == TdsEnums.SQLJSONTYPE)
+ {
+ // TODO : Performance and BOM check. Saurabh
+ byte[] jsonAsBytes = Encoding.UTF8.GetBytes((string)value);
+ WriteInt(jsonAsBytes.Length, stateObj);
+ return stateObj.WriteByteArray(jsonAsBytes, jsonAsBytes.Length, 0, canAccumulate: false);
+ }
+ else if (IsBOMNeeded(type, value))
{
WriteInt(actualLength + 2, stateObj); // chunk length
WriteShort(TdsEnums.XMLUNICODEBOM, stateObj);
@@ -13283,7 +13332,7 @@ internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsPars
WriteInt(unchecked((int)TdsEnums.VARLONGNULL), stateObj);
}
}
- else if (type.NullableType == TdsEnums.SQLXMLTYPE || unknownLength)
+ else if (type.NullableType is TdsEnums.SQLXMLTYPE or TdsEnums.SQLJSONTYPE || unknownLength)
{
WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlJson.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlJson.cs
new file mode 100644
index 0000000000..4c90145aac
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlJson.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Data.SqlClient
+{
+ internal class SqlJson
+ {
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
index 3fbd9b112a..db264a39ea 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
@@ -190,6 +190,8 @@ internal static MetaType GetMetaTypeFromSqlDbType(SqlDbType target, bool isMulti
return MetaImage;
case SqlDbType.Int:
return s_metaInt;
+ case (SqlDbType)35:
+ return s_metaJson;
case SqlDbType.Money:
return s_metaMoney;
case SqlDbType.NChar:
@@ -852,6 +854,8 @@ internal static MetaType GetSqlDataType(int tdsType, uint userType, int length)
return MetaUdt;
case TdsEnums.SQLXMLTYPE:
return MetaXml;
+ case TdsEnums.SQLJSONTYPE:
+ return s_metaJson;
case TdsEnums.SQLTABLE:
return s_metaTable;
case TdsEnums.SQLDATE:
@@ -968,6 +972,8 @@ internal static string GetStringFromXml(XmlReader xmlreader)
internal static readonly MetaType MetaDateTimeOffset = new(255, 7, -1, false, false, false, TdsEnums.SQLDATETIMEOFFSET, TdsEnums.SQLDATETIMEOFFSET, MetaTypeName.DATETIMEOFFSET, typeof(System.DateTimeOffset), typeof(System.DateTimeOffset), SqlDbType.DateTimeOffset, DbType.DateTimeOffset, 1);
+ private static readonly MetaType s_metaJson = new(255, 255, -1, false, true, true, TdsEnums.SQLJSONTYPE, TdsEnums.SQLJSONTYPE, MetaTypeName.JSON, typeof(string), typeof(SqlJson), (SqlDbType)35, DbType.String, 0);
+
public static TdsDateTime FromDateTime(DateTime dateTime, byte cb)
{
SqlDateTime sqlDateTime;
@@ -1054,6 +1060,7 @@ private static class MetaTypeName
public const string TIME = "time";
public const string DATETIME2 = "datetime2";
public const string DATETIMEOFFSET = "datetimeoffset";
+ public const string JSON = "json";
}
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs
index a7c7fa58e1..b6955d6e9a 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs
@@ -1563,6 +1563,7 @@ internal int GetActualSize()
case SqlDbType.NVarChar:
case SqlDbType.NText:
case SqlDbType.Xml:
+ case (SqlDbType)35:
{
coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType)) : 0;
_actualSize = (ShouldSerializeSize() ? Size : 0);
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
index 8c7119a695..0604407345 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
@@ -238,6 +238,7 @@ public enum EnvChangeType : byte
public const byte FEATUREEXT_DATACLASSIFICATION = 0x09;
public const byte FEATUREEXT_UTF8SUPPORT = 0x0A;
public const byte FEATUREEXT_SQLDNSCACHING = 0x0B;
+ public const byte FEATUREEXT_JSONSUPPORT = 0x0D;
[Flags]
public enum FeatureExtension : uint
@@ -250,7 +251,8 @@ public enum FeatureExtension : uint
AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1),
DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1),
UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1),
- SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1)
+ SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1),
+ JsonSupport = 1 << (TdsEnums.FEATUREEXT_JSONSUPPORT - 1)
}
public const uint UTF8_IN_TDSCOLLATION = 0x4000000;
@@ -488,6 +490,8 @@ public enum ActiveDirectoryWorkflow : byte
public const int SQLDATETIME2 = 0x2a;
public const int SQLDATETIMEOFFSET = 0x2b;
+ public const int SQLJSONTYPE = 0xf4;
+
public const int DEFAULT_VARTIME_SCALE = 7;
//Partially length prefixed datatypes constants. These apply to XMLTYPE, BIGVARCHRTYPE,
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs
index 0c9320f512..bbe039d426 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs
@@ -77,5 +77,10 @@ public interface ITDSServerSession
/// Indicates whether this session supports transport-level recovery
///
bool IsSessionRecoveryEnabled { get; set; }
+
+ ///
+ /// Indicates whether the client supports Json column type
+ ///
+ bool IsJsonSupportEnabled { get; set; }
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs
index 535304964d..1a6e60fd08 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs
@@ -230,6 +230,12 @@ public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, T
// Save the fed auth library to be used
(session as GenericTDSServerSession).FederatedAuthenticationLibrary = federatedAuthenticationOption.Library;
+ break;
+ }
+ case TDSFeatureID.JsonSupport:
+ {
+ // Enable Json Support
+ session.IsJsonSupportEnabled = true;
break;
}
default:
@@ -544,6 +550,32 @@ protected virtual TDSMessageCollection OnAuthenticationCompleted(ITDSServerSessi
responseMessage.Add(featureExtActToken);
}
+ // Check if Json is supported
+ if (session.IsJsonSupportEnabled)
+ {
+ // Create ack data (1 byte: Version number)
+ byte[] data = new byte[1];
+ data[0] = (byte)1;
+
+ // Create Json support as a generic feature extension option
+ TDSFeatureExtAckGenericOption jsonSupportOption = new TDSFeatureExtAckGenericOption(TDSFeatureID.JsonSupport, (uint)data.Length, data);
+
+ // Look for feature extension token
+ TDSFeatureExtAckToken featureExtAckToken = (TDSFeatureExtAckToken)responseMessage.Where(t => t is TDSFeatureExtAckToken).FirstOrDefault();
+
+ if (featureExtAckToken == null)
+ {
+ // Create feature extension ack token
+ featureExtAckToken = new TDSFeatureExtAckToken(jsonSupportOption);
+ responseMessage.Add(featureExtAckToken);
+ }
+ else
+ {
+ // Update the existing token
+ featureExtAckToken.Options.Add(jsonSupportOption);
+ }
+ }
+
// Create DONE token
TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final);
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs
index 4c1de9f986..3272036e15 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs
@@ -108,6 +108,11 @@ public class GenericTDSServerSession : ITDSServerSession
///
public bool IsSessionRecoveryEnabled { get; set; }
+ ///
+ /// Indicates whether this session supports Json column type
+ ///
+ public bool IsJsonSupportEnabled { get; set; }
+
#region Session Options
///
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSFeatureID.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSFeatureID.cs
index bac86b591c..eb84a631d0 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSFeatureID.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSFeatureID.cs
@@ -19,6 +19,11 @@ public enum TDSFeatureID : byte
///
FederatedAuthentication = 0x02,
+ ///
+ /// JSON Support
+ ///
+ JsonSupport = 0x0D,
+
///
/// End of the list
///
diff --git a/tools/props/Versions.props b/tools/props/Versions.props
index 022c9a4c7a..30bf6f4b64 100644
--- a/tools/props/Versions.props
+++ b/tools/props/Versions.props
@@ -27,8 +27,8 @@
- 1.11.2
- 4.60.3
+ 1.12.0
+ 4.61.3
7.5.0
7.5.0
4.5.1