From 17e21c1e676c17e80ebfa9c4ce39d9de16c703fb Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 28 Nov 2024 13:16:47 -0800 Subject: [PATCH] JSON feature extension --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 8 ++++ .../sqlserver/jdbc/SQLServerConnection.java | 43 +++++++++++++++++++ .../sqlserver/jdbc/SQLServerResource.java | 2 + 3 files changed, 53 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index f51a5430c..e3148b2b4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -169,6 +169,11 @@ final class TDS { static final byte TDS_FEATURE_EXT_AZURESQLDNSCACHING = 0x0B; static final byte TDS_FEATURE_EXT_SESSIONRECOVERY = 0x01; + // JSON support + static final byte TDS_FEATURE_EXT_JSONSUPPORT = 0x0D; + static final byte JSONSUPPORT_NOT_SUPPORTED = 0x00; + static final byte MAX_JSONSUPPORT_VERSION = 0x01; + static final int TDS_TVP = 0xF3; static final int TVP_ROW = 0x01; static final int TVP_NULL_TOKEN = 0xFFFF; @@ -237,6 +242,9 @@ static final String getTokenName(int tdsTokenType) { return "TDS_FEATURE_EXT_AZURESQLDNSCACHING (0x0B)"; case TDS_FEATURE_EXT_SESSIONRECOVERY: return "TDS_FEATURE_EXT_SESSIONRECOVERY (0x01)"; + case TDS_FEATURE_EXT_JSONSUPPORT: + return "TDS_FEATURE_EXT_JSONSUPPORT (0x0D)"; + default: return "unknown token (0x" + Integer.toHexString(tdsTokenType).toUpperCase() + ")"; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 1571f5bec..3cc8141c9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1021,6 +1021,16 @@ byte getServerSupportedDataClassificationVersion() { return serverSupportedDataClassificationVersion; } + /** whether server supports JSON */ + private boolean serverSupportsJSON = false; + + /** server supported JSON version */ + private byte serverSupportedJSONVersion = TDS.JSONSUPPORT_NOT_SUPPORTED; + + boolean getServerSupportsJSON() { + return serverSupportsJSON; + } + /** Boolean that indicates whether LOB objects created by this connection should be loaded into memory */ private boolean delayLoadingLobs = SQLServerDriverBooleanProperty.DELAY_LOADING_LOBS.getDefaultValue(); @@ -5340,6 +5350,17 @@ int writeDNSCacheFeatureRequest(boolean write, /* if false just calculates the l return len; } + int writeJSONSupportFeatureRequest(boolean write, /* if false just calculates the length */ + TDSWriter tdsWriter) throws SQLServerException { + int len = 6; // 1byte = featureID, 4bytes = featureData length, 1 bytes = Version + if (write) { + tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_JSONSUPPORT); + tdsWriter.writeInt(1); + tdsWriter.writeByte(TDS.MAX_JSONSUPPORT_VERSION); + } + return len; + } + int writeIdleConnectionResiliencyRequest(boolean write, TDSWriter tdsWriter) throws SQLServerException { SessionStateTable ssTable = sessionRecovery.getSessionStateTable(); int len = 1; @@ -6469,6 +6490,24 @@ private void onFeatureExtAck(byte featureId, byte[] data) throws SQLServerExcept sessionRecovery.setConnectionRecoveryPossible(true); break; } + + case TDS.TDS_FEATURE_EXT_JSONSUPPORT: { + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.fine(toString() + " Received feature extension acknowledgement for JSON Support."); + } + + if (1 != data.length) { + throw new SQLServerException(SQLServerException.getErrString("R_unknownJSONSupportValue"), null); + } + + serverSupportedJSONVersion = data[0]; + if (0 == serverSupportedJSONVersion || serverSupportedJSONVersion > TDS.MAX_JSONSUPPORT_VERSION) { + throw new SQLServerException(SQLServerException.getErrString("R_InvalidJSONVersionNumber"), null); + } + serverSupportsJSON = true; + break; + } + default: { // Unknown feature ack throw new SQLServerException(SQLServerException.getErrString("R_UnknownFeatureAck"), null); @@ -6768,6 +6807,9 @@ final boolean complete(LogonCommand logonCommand, TDSReader tdsReader) throws SQ len = len + writeDNSCacheFeatureRequest(false, tdsWriter); + // request JSON support + len += writeJSONSupportFeatureRequest(false, tdsWriter); + len = len + 1; // add 1 to length because of FeatureEx terminator // Idle Connection Resiliency is requested @@ -6964,6 +7006,7 @@ final boolean complete(LogonCommand logonCommand, TDSReader tdsReader) throws SQ writeDataClassificationFeatureRequest(true, tdsWriter); writeUTF8SupportFeatureRequest(true, tdsWriter); writeDNSCacheFeatureRequest(true, tdsWriter); + writeJSONSupportFeatureRequest(true, tdsWriter); // Idle Connection Resiliency is requested if (connectRetryCount > 0) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index c9d875e58..435e04296 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -311,6 +311,7 @@ protected Object[][] getContents() { {"R_StreamingDataTypeAE", "Data of length greater than {0} is not supported in encrypted {1} column."}, {"R_AE_NotSupportedByServer", "SQL Server in use does not support column encryption."}, {"R_InvalidAEVersionNumber", "Received invalid version number \"{0}\" for Always Encrypted."}, // From server + {"R_InvalidJSONVersionNumber", "Received invalid version number \"{0}\" for JSON."}, {"R_NullEncryptedColumnEncryptionKey", "Internal error. Encrypted column encryption key cannot be null."}, {"R_EmptyEncryptedColumnEncryptionKey", "Internal error. Empty encrypted column encryption key specified."}, {"R_InvalidMasterKeyDetails", "Invalid master key details specified."}, @@ -470,6 +471,7 @@ protected Object[][] getContents() { {"R_InvalidDataClsVersionNumber", "Invalid version number {0} for Data Classification."}, // From Server {"R_unknownUTF8SupportValue", "Unknown value for UTF8 support."}, {"R_unknownAzureSQLDNSCachingValue", "Unknown value for Azure SQL DNS Caching."}, + {"R_unknownJSONSupportValue", "Unknown value for JSON support."}, {"R_illegalWKT", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, {"R_illegalTypeForGeometry", "{0} is not supported for Geometry."}, {"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."},