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 f4df2b11f9..d976bb06fc 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 @@ -200,6 +200,9 @@ internal bool IsDNSCachingBeforeRedirectSupported internal SQLDNSInfo pendingSQLDNSObject = null; + // Json Support Flag + internal bool IsJsonSupportEnabled = false; + // TCE flags internal byte _tceVersionSupported; @@ -1362,7 +1365,9 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The SQLDNSCaching feature is implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; - +#if DEBUG + requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport; +#endif _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt); } @@ -2811,6 +2816,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 > TdsEnums.MAX_SUPPORTED_JSON_VERSION) + { + SqlClientEventSource.Log.TryTraceEvent(" {0}, Invalid version number for JSONSUPPORT", ObjectID); + throw SQL.ParsingError(); + } + 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 4cee4ff95c..edeecb6c9f 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 @@ -8411,6 +8411,24 @@ 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); + + // Feature Data Length + WriteInt(1, _physicalStateObj); + + _physicalStateObj.WriteByte(TdsEnums.MAX_SUPPORTED_JSON_VERSION); + } + + return len; + } + private void WriteLoginData(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, @@ -8723,6 +8741,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures, length += WriteSQLDNSCachingFeatureRequest(write); } + if ((requestedFeatures & TdsEnums.FeatureExtension.JsonSupport) != 0) + { + length += WriteJsonSupportFeatureRequest(write); + } + length++; // for terminator if (write) { 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 0aaa4a6d93..cdb7ba2173 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 @@ -212,6 +212,9 @@ internal bool IsDNSCachingBeforeRedirectSupported internal SQLDNSInfo pendingSQLDNSObject = null; + // Json Support Flag + internal bool IsJsonSupportEnabled = false; + // TCE flags internal byte _tceVersionSupported; @@ -1638,6 +1641,10 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The SQLDNSCaching feature is implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; +#if DEBUG + requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport; +#endif + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, _originalNetworkAddressInfo, encrypt); } @@ -3239,6 +3246,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 > TdsEnums.MAX_SUPPORTED_JSON_VERSION) + { + SqlClientEventSource.Log.TryTraceEvent(" {0}, Invalid version number for JSONSUPPORT", ObjectID); + throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); + } + 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 fb0884bb36..f9b1306e42 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 @@ -9252,6 +9252,24 @@ 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); + + // Feature Data Length + WriteInt(1, _physicalStateObj); + + _physicalStateObj.WriteByte(TdsEnums.MAX_SUPPORTED_JSON_VERSION); + } + + return len; + } + private void WriteLoginData(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, @@ -9581,6 +9599,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures, length += WriteSQLDNSCachingFeatureRequest(write); } + if ((requestedFeatures & TdsEnums.FeatureExtension.JsonSupport) != 0) + { + length += WriteJsonSupportFeatureRequest(write); + } + length++; // for terminator if (write) { 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..685002790c 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; @@ -994,6 +996,9 @@ internal enum FedAuthInfoId : byte internal const byte DATA_CLASSIFICATION_VERSION_WITHOUT_RANK_SUPPORT = 0x01; internal const byte DATA_CLASSIFICATION_VERSION_MAX_SUPPORTED = 0x02; + // JSON Support constants + internal const byte MAX_SUPPORTED_JSON_VERSION = 0x01; + // TCE Related constants internal const byte MAX_SUPPORTED_TCE_VERSION = 0x03; // max version internal const byte MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT = 0x02; // min version with enclave support 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..e0c243dcc8 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 @@ -232,6 +232,14 @@ public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, T break; } +#if DEBUG + case TDSFeatureID.JsonSupport: + { + // Enable Json Support + session.IsJsonSupportEnabled = true; + break; + } +#endif default: { // Do nothing @@ -544,6 +552,34 @@ protected virtual TDSMessageCollection OnAuthenticationCompleted(ITDSServerSessi responseMessage.Add(featureExtActToken); } +#if DEBUG + // 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); + } + } +#endif + // 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 ///