Skip to content

Commit 2aa807e

Browse files
authored
Added getter for sensitivity rank and check for version which added sensitivity rank support (#1373)
1 parent c61f652 commit 2aa807e

File tree

6 files changed

+120
-73
lines changed

6 files changed

+120
-73
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java

+15-17
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ final class TDS {
126126
static final byte TDS_FEATURE_EXT_DATACLASSIFICATION = 0x09;
127127
static final byte DATA_CLASSIFICATION_NOT_ENABLED = 0x00;
128128
static final byte MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION = 0x02;
129+
static final byte DATA_CLASSIFICATION_VERSION_ADDED_RANK_SUPPORT = 0x02;
129130

130131
static final int AES_256_CBC = 1;
131132
static final int AEAD_AES_256_CBC_HMAC_SHA256 = 2;
@@ -3613,11 +3614,10 @@ void writeDateTimeOffset(Object value, int scale, SSType destSSType) throws SQLS
36133614
int minutesOffset;
36143615

36153616
/*
3616-
* Out of all the supported temporal datatypes, DateTimeOffset is the only datatype that doesn't
3617-
* allow direct casting from java.sql.timestamp (which was created from a String).
3618-
* DateTimeOffset was never required to be constructed from a String, but with the
3619-
* introduction of extended bulk copy support for Azure DW, we now need to support this scenario.
3620-
* Parse the DTO as string if it's coming from a CSV.
3617+
* Out of all the supported temporal datatypes, DateTimeOffset is the only datatype that doesn't allow direct
3618+
* casting from java.sql.timestamp (which was created from a String). DateTimeOffset was never required to be
3619+
* constructed from a String, but with the introduction of extended bulk copy support for Azure DW, we now need
3620+
* to support this scenario. Parse the DTO as string if it's coming from a CSV.
36213621
*/
36223622
if (value instanceof String) {
36233623
// expected format: YYYY-MM-DD hh:mm:ss[.nnnnnnn] [{+|-}hh:mm]
@@ -3628,8 +3628,8 @@ void writeDateTimeOffset(Object value, int scale, SSType destSSType) throws SQLS
36283628
String offsetString = stringValue.substring(lastColon - 3);
36293629

36303630
/*
3631-
* At this point, offsetString should look like +hh:mm or -hh:mm. Otherwise, the optional offset
3632-
* value has not been provided. Parse accordingly.
3631+
* At this point, offsetString should look like +hh:mm or -hh:mm. Otherwise, the optional offset value
3632+
* has not been provided. Parse accordingly.
36333633
*/
36343634
String timestampString;
36353635

@@ -3646,11 +3646,10 @@ void writeDateTimeOffset(Object value, int scale, SSType destSSType) throws SQLS
36463646
}
36473647

36483648
/*
3649-
* If the target data type is DATETIMEOFFSET, then use UTC for the calendar that
3650-
* will hold the value, since writeRPCDateTimeOffset expects a UTC calendar.
3651-
* Otherwise, when converting from DATETIMEOFFSET to other temporal data types,
3652-
* use a local time zone determined by the minutes offset of the value, since
3653-
* the writers for those types expect local calendars.
3649+
* If the target data type is DATETIMEOFFSET, then use UTC for the calendar that will hold the value,
3650+
* since writeRPCDateTimeOffset expects a UTC calendar. Otherwise, when converting from DATETIMEOFFSET
3651+
* to other temporal data types, use a local time zone determined by the minutes offset of the value,
3652+
* since the writers for those types expect local calendars.
36543653
*/
36553654
timeZone = (SSType.DATETIMEOFFSET == destSSType) ? UTC.timeZone
36563655
: new SimpleTimeZone(minutesOffset * 60 * 1000, "");
@@ -3689,11 +3688,10 @@ void writeDateTimeOffset(Object value, int scale, SSType destSSType) throws SQLS
36893688
minutesOffset = dtoValue.getMinutesOffset();
36903689

36913690
/*
3692-
* If the target data type is DATETIMEOFFSET, then use UTC for the calendar that
3693-
* will hold the value, since writeRPCDateTimeOffset expects a UTC calendar.
3694-
* Otherwise, when converting from DATETIMEOFFSET to other temporal data types,
3695-
* use a local time zone determined by the minutes offset of the value, since
3696-
* the writers for those types expect local calendars.
3691+
* If the target data type is DATETIMEOFFSET, then use UTC for the calendar that will hold the value, since
3692+
* writeRPCDateTimeOffset expects a UTC calendar. Otherwise, when converting from DATETIMEOFFSET to other
3693+
* temporal data types, use a local time zone determined by the minutes offset of the value, since the
3694+
* writers for those types expect local calendars.
36973695
*/
36983696
timeZone = (SSType.DATETIMEOFFSET == destSSType) ? UTC.timeZone
36993697
: new SimpleTimeZone(minutesOffset * 60 * 1000, "");

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java

-1
Original file line numberDiff line numberDiff line change
@@ -2557,7 +2557,6 @@ public boolean locatorsUpdateCopy() throws SQLException {
25572557
* @return the database compatibility level value (from sys.databases table).
25582558
* @throws SQLException
25592559
* if error getting compatability level
2560-
25612560
*/
25622561
public int getDatabaseCompatibilityLevel() throws SQLException {
25632562
checkClosed();

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java

-30
Original file line numberDiff line numberDiff line change
@@ -455,36 +455,6 @@ public String toString() {
455455
}
456456

457457

458-
enum SensitivityRank {
459-
NOT_DEFINED(-1),
460-
NONE(0),
461-
LOW(10),
462-
MEDIUM(20),
463-
HIGH(30),
464-
CRITICAL(40);
465-
466-
private static final SensitivityRank[] VALUES = values();
467-
private int rank;
468-
469-
private SensitivityRank(int rank) {
470-
this.rank = rank;
471-
}
472-
473-
public int getValue() {
474-
return rank;
475-
}
476-
477-
static boolean isValid(int rank) throws SQLServerException {
478-
for (SensitivityRank r : VALUES) {
479-
if (r.getValue() == rank) {
480-
return true;
481-
}
482-
}
483-
return false;
484-
}
485-
}
486-
487-
488458
/**
489459
* Provides methods to connect to a SQL Server database and to obtain information about the JDBC driver.
490460
*/

src/main/java/com/microsoft/sqlserver/jdbc/StreamColumns.java

+24-10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.microsoft.sqlserver.jdbc.dataclassification.InformationType;
1313
import com.microsoft.sqlserver.jdbc.dataclassification.Label;
1414
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification;
15+
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification.SensitivityRank;
1516
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityProperty;
1617

1718

@@ -29,6 +30,8 @@ final class StreamColumns extends StreamPacket {
2930

3031
private boolean shouldHonorAEForRead = false;
3132

33+
private boolean sensitivityRankSupported = false;
34+
3235
/* Returns the CekTable */
3336
CekTable getCekTable() {
3437
return cekTable;
@@ -274,8 +277,12 @@ private SensitivityClassification processDataClassification(TDSReader tdsReader)
274277
informationTypes.add(readSensitivityInformationType(tdsReader));
275278
}
276279

280+
sensitivityRankSupported = tdsReader
281+
.getServerSupportedDataClassificationVersion() >= TDS.DATA_CLASSIFICATION_VERSION_ADDED_RANK_SUPPORT;
282+
283+
// get sensitivity rank if supported
277284
int sensitivityRank = SensitivityRank.NOT_DEFINED.getValue();
278-
if (TDS.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION <= tdsReader.getServerSupportedDataClassificationVersion()) {
285+
if (sensitivityRankSupported) {
279286
sensitivityRank = tdsReader.readInt();
280287
if (!SensitivityRank.isValid(sensitivityRank)) {
281288
tdsReader.throwInvalidTDS();
@@ -311,21 +318,28 @@ private SensitivityClassification processDataClassification(TDSReader tdsReader)
311318
informationType = informationTypes.get(informationTypeIndex);
312319
}
313320

314-
if (TDS.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION <= tdsReader
315-
.getServerSupportedDataClassificationVersion()) {
316-
sensitivityRank = tdsReader.readInt();
317-
if (!SensitivityRank.isValid(sensitivityRank)) {
321+
int sensitivityRankProperty = SensitivityRank.NOT_DEFINED.getValue();
322+
if (sensitivityRankSupported) {
323+
sensitivityRankProperty = tdsReader.readInt();
324+
if (!SensitivityRank.isValid(sensitivityRankProperty)) {
318325
tdsReader.throwInvalidTDS();
319326
}
327+
// add sensitivity properties for the source
328+
sensitivityProperties.add(new SensitivityProperty(label, informationType, sensitivityRankProperty));
329+
} else {
330+
sensitivityProperties.add(new SensitivityProperty(label, informationType));
320331
}
321-
322-
// add sensitivity properties for the source
323-
sensitivityProperties.add(new SensitivityProperty(label, informationType, sensitivityRank));
324332
}
325333
columnSensitivities.add(new ColumnSensitivity(sensitivityProperties));
326334
}
327-
sensitivityClassification = new SensitivityClassification(sensitivityLabels, informationTypes,
328-
columnSensitivities);
335+
if (sensitivityRankSupported) {
336+
sensitivityClassification = new SensitivityClassification(sensitivityLabels, informationTypes,
337+
columnSensitivities, sensitivityRank);
338+
} else {
339+
sensitivityClassification = new SensitivityClassification(sensitivityLabels, informationTypes,
340+
columnSensitivities);
341+
}
342+
329343
return sensitivityClassification;
330344
}
331345

src/main/java/com/microsoft/sqlserver/jdbc/dataclassification/SensitivityClassification.java

+63-5
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,54 @@
88
import java.util.ArrayList;
99
import java.util.List;
1010

11+
import com.microsoft.sqlserver.jdbc.SQLServerException;
12+
1113

1214
/**
13-
* Provides the functionlity to retrieve Sensitivity Classification data as received from SQL Server for the active
15+
* Provides the functionality to retrieve Sensitivity Classification data as received from SQL Server for the active
1416
* resultSet
1517
*/
1618
public class SensitivityClassification {
19+
20+
public enum SensitivityRank {
21+
NOT_DEFINED(-1),
22+
NONE(0),
23+
LOW(10),
24+
MEDIUM(20),
25+
HIGH(30),
26+
CRITICAL(40);
27+
28+
private static final SensitivityRank[] VALUES = values();
29+
private int rank;
30+
31+
private SensitivityRank(int rank) {
32+
this.rank = rank;
33+
}
34+
35+
public int getValue() {
36+
return rank;
37+
}
38+
39+
public static boolean isValid(int rank) throws SQLServerException {
40+
for (SensitivityRank r : VALUES) {
41+
if (r.getValue() == rank) {
42+
return true;
43+
}
44+
}
45+
return false;
46+
}
47+
}
48+
1749
private List<Label> labels;
1850
private List<InformationType> informationTypes;
1951
private List<ColumnSensitivity> columnSensitivities;
52+
private int sensitivityRank;
53+
54+
/*
55+
* Creating new ArrayList here assures that 'informationTypes' and 'labels' properties will not be null. The Count
56+
* of the ColumnSensitivities property will be equal to the number of output columns for the query result set.
57+
*/
2058

21-
// Creating new ArrayList here assures that 'informationTypes' and 'labels'
22-
// properties will not be null.
23-
// The Count of the ColumnSensitivities property will be equal to the number
24-
// of output columns for the query result set.
2559
/**
2660
* Constructs a <code>SensitivityClassification</code> Object
2761
*
@@ -39,6 +73,26 @@ public SensitivityClassification(List<Label> labels, List<InformationType> infor
3973
this.columnSensitivities = new ArrayList<>(columnSensitivity);
4074
}
4175

76+
/**
77+
* Constructs a <code>SensitivityClassification</code> Object
78+
*
79+
* @param labels
80+
* Labels as received from SQL Server
81+
* @param informationTypes
82+
* Information Types as received from SQL Server
83+
* @param columnSensitivity
84+
* Column Sensitivities as received from SQL Server
85+
* @param sensitivityRank
86+
* Sensitivity rank as received from SQL Server
87+
*/
88+
public SensitivityClassification(List<Label> labels, List<InformationType> informationTypes,
89+
List<ColumnSensitivity> columnSensitivity, int sensitivityRank) {
90+
this.labels = new ArrayList<>(labels);
91+
this.informationTypes = new ArrayList<>(informationTypes);
92+
this.columnSensitivities = new ArrayList<>(columnSensitivity);
93+
this.sensitivityRank = sensitivityRank;
94+
}
95+
4296
/**
4397
* Returns the labels for this <code>SensitivityClassification</code> Object
4498
*
@@ -65,4 +119,8 @@ public List<InformationType> getInformationTypes() {
65119
public List<ColumnSensitivity> getColumnSensitivities() {
66120
return columnSensitivities;
67121
}
122+
123+
public int getSensitivityRank() {
124+
return sensitivityRank;
125+
}
68126
}

src/test/java/com/microsoft/sqlserver/jdbc/resultset/DataClassificationTest.java

+18-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.microsoft.sqlserver.jdbc.dataclassification.InformationType;
2828
import com.microsoft.sqlserver.jdbc.dataclassification.Label;
2929
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityProperty;
30+
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification.SensitivityRank;
3031
import com.microsoft.sqlserver.testframework.AbstractSQLGenerator;
3132
import com.microsoft.sqlserver.testframework.AbstractTest;
3233
import com.microsoft.sqlserver.testframework.Constants;
@@ -39,31 +40,35 @@ public class DataClassificationTest extends AbstractTest {
3940

4041
private static final String addSensitivitySql = "ADD SENSITIVITY CLASSIFICATION TO %s.%s WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='%s', INFORMATION_TYPE_ID='%s'%s)";
4142
private static final String sensitivityRankSql = ", RANK=%s";
42-
private static String sensitivityRank[][] = {{"NONE", "0"}, {"LOW", "10"}, {"MEDIUM", "20"}, {"HIGH", "30"},
43-
{"CRITICAL", "40"}};
4443

4544
/**
4645
* Tests data classification metadata information from SQL Server
4746
*
47+
* TODO: remove xAzureSQLDW tag once issue on server is fixed (currently DW not returning rank info) VSO issue 12931
48+
*
4849
* @throws Exception
4950
*/
5051
@Tag(Constants.xSQLv12)
5152
@Tag(Constants.xSQLv14)
53+
@Tag(Constants.xAzureSQLDW)
5254
@Test
5355
public void testDataClassificationMetadata() throws Exception {
5456
try (Statement stmt = connection.createStatement();) {
5557
if (!TestUtils.serverSupportsDataClassification(stmt)) {
5658
fail(TestResource.getResource("R_dataClassificationNotSupported"));
5759
}
5860

59-
for (int i = 0; i < sensitivityRank.length; i++) {
60-
createTable(connection, stmt);
61-
addSensitivity(connection, stmt, sensitivityRank[i][0]);
62-
insertData(connection, stmt);
63-
try (SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName)) {
64-
verifySensitivityClassification(rs, Integer.parseInt(sensitivityRank[i][1]));
61+
for (SensitivityRank i : SensitivityRank.values()) {
62+
if (SensitivityRank.NOT_DEFINED != i) {
63+
createTable(connection, stmt);
64+
addSensitivity(connection, stmt, i.toString());
65+
insertData(connection, stmt);
66+
try (SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("SELECT * FROM " + tableName
67+
+ "ORDER BY [CompanyName], [ContactTitle], [CountryName], [Phone], [Fax]")) {
68+
verifySensitivityClassification(rs, i.getValue());
69+
}
70+
dropTable();
6571
}
66-
dropTable();
6772
}
6873
}
6974
}
@@ -173,7 +178,10 @@ private void verifySensitivityClassification(SQLServerResultSet rs, int rank) th
173178

174179
verifyLabel(sp.getLabel());
175180
verifyInfoType(sp.getInformationType(), columnPos);
176-
assertEquals(sp.getSensitivityRank(), rank, TestResource.getResource("R_valuesAreDifferent"));
181+
assertEquals(rank, sp.getSensitivityRank(), TestResource.getResource("R_valuesAreDifferent"));
182+
183+
int sensitivityRank = rs.getSensitivityClassification().getSensitivityRank();
184+
assertEquals(rank, sensitivityRank, TestResource.getResource("R_valuesAreDifferent"));
177185
}
178186
}
179187
}

0 commit comments

Comments
 (0)