package com.microsoft.sqlserver.jdbc;
import java.util.ArrayList;
import java.util.List;
import com.microsoft.sqlserver.jdbc.dataclassification.ColumnSensitivity;
import com.microsoft.sqlserver.jdbc.dataclassification.InformationType;
import com.microsoft.sqlserver.jdbc.dataclassification.Label;
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification;
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification.SensitivityRank;
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityProperty;
final class StreamColumns extends StreamPacket {
private Column[] columns;
private CekTable cekTable = null;
private boolean shouldHonorAEForRead = false;
private boolean sensitivityRankSupported = false;
CekTable getCekTable() {
return cekTable;
}
StreamColumns() {
super(TDS.TDS_COLMETADATA);
}
StreamColumns(boolean honorAE) {
super(TDS.TDS_COLMETADATA);
shouldHonorAEForRead = honorAE;
}
CekTableEntry readCEKTableEntry(TDSReader tdsReader) throws SQLServerException {
int databaseId = tdsReader.readInt();
int cekId = tdsReader.readInt();
int cekVersion = tdsReader.readInt();
byte[] cekMdVersion = new byte[8];
tdsReader.readBytes(cekMdVersion, 0, 8);
int cekValueCount = tdsReader.readUnsignedByte();
CekTableEntry cekTableEntry = new CekTableEntry(cekValueCount);
for (int i = 0; i < cekValueCount; i++) {
short encryptedCEKlength = tdsReader.readShort();
byte[] encryptedCek = new byte[encryptedCEKlength];
tdsReader.readBytes(encryptedCek, 0, encryptedCEKlength);
int keyStoreLength = tdsReader.readUnsignedByte();
String keyStoreName = tdsReader.readUnicodeString(keyStoreLength);
int keyPathLength = tdsReader.readShort();
String keyPath = tdsReader.readUnicodeString(keyPathLength);
int algorithmLength = tdsReader.readUnsignedByte();
String algorithmName = tdsReader.readUnicodeString(algorithmLength);
cekTableEntry.add(encryptedCek, databaseId, cekId, cekVersion, cekMdVersion, keyPath, keyStoreName,
algorithmName);
}
return cekTableEntry;
}
void readCEKTable(TDSReader tdsReader) throws SQLServerException {
int tableSize = tdsReader.readShort();
if (0 != tableSize) {
cekTable = new CekTable(tableSize);
for (int i = 0; i < tableSize; i++) {
cekTable.setCekTableEntry(i, readCEKTableEntry(tdsReader));
}
}
}
CryptoMetadata readCryptoMetadata(TDSReader tdsReader) throws SQLServerException {
short ordinal = 0;
if (null != cekTable) {
ordinal = tdsReader.readShort();
}
TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, false);
byte algorithmId = (byte) tdsReader.readUnsignedByte();
String algorithmName = null;
if ((byte) TDS.CUSTOM_CIPHER_ALGORITHM_ID == algorithmId) {
int nameSize = tdsReader.readUnsignedByte();
algorithmName = tdsReader.readUnicodeString(nameSize);
}
byte encryptionType = (byte) tdsReader.readUnsignedByte();
byte normalizationRuleVersion = (byte) tdsReader.readUnsignedByte();
CryptoMetadata cryptoMeta = new CryptoMetadata((cekTable == null) ? null : cekTable.getCekTableEntry(ordinal),
ordinal, algorithmId, algorithmName, encryptionType, normalizationRuleVersion);
cryptoMeta.setBaseTypeInfo(typeInfo);
return cryptoMeta;
}
void setFromTDS(TDSReader tdsReader) throws SQLServerException {
if (TDS.TDS_COLMETADATA != tdsReader.readUnsignedByte())
assert false;
int nTotColumns = tdsReader.readUnsignedShort();
if (0xFFFF == nTotColumns)
return;
if (tdsReader.getServerSupportsColumnEncryption()) {
readCEKTable(tdsReader);
}
this.columns = new Column[nTotColumns];
for (int numColumns = 0; numColumns < nTotColumns; numColumns++) {
TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, true);
SQLIdentifier tableName = new SQLIdentifier();
if (SSType.TEXT == typeInfo.getSSType() || SSType.NTEXT == typeInfo.getSSType()
|| SSType.IMAGE == typeInfo.getSSType()) {
tableName = tdsReader.readSQLIdentifier();
}
CryptoMetadata cryptoMeta = null;
if (tdsReader.getServerSupportsColumnEncryption() && typeInfo.isEncrypted()) {
cryptoMeta = readCryptoMetadata(tdsReader);
cryptoMeta.baseTypeInfo.setFlags(typeInfo.getFlagsAsShort());
typeInfo.setSQLCollation(cryptoMeta.baseTypeInfo.getSQLCollation());
}
String columnName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte());
if (shouldHonorAEForRead) {
this.columns[numColumns] = new Column(typeInfo, columnName, tableName, cryptoMeta);
} else {
this.columns[numColumns] = new Column(typeInfo, columnName, tableName, null);
}
}
if (tdsReader.getServerSupportsDataClassification()
&& tdsReader.peekTokenType() == TDS.TDS_SQLDATACLASSIFICATION) {
tdsReader.trySetSensitivityClassification(processDataClassification(tdsReader));
}
}
private String readByteString(TDSReader tdsReader) throws SQLServerException {
String value = "";
int byteLen = (int) tdsReader.readUnsignedByte();
value = tdsReader.readUnicodeString(byteLen);
return value;
}
private Label readSensitivityLabel(TDSReader tdsReader) throws SQLServerException {
String name = readByteString(tdsReader);
String id = readByteString(tdsReader);
return new Label(name, id);
}
private InformationType readSensitivityInformationType(TDSReader tdsReader) throws SQLServerException {
String name = readByteString(tdsReader);
String id = readByteString(tdsReader);
return new InformationType(name, id);
}
private SensitivityClassification processDataClassification(TDSReader tdsReader) throws SQLServerException {
if (!tdsReader.getServerSupportsDataClassification()) {
tdsReader.throwInvalidTDS();
}
int dataClassificationToken = tdsReader.readUnsignedByte();
assert dataClassificationToken == TDS.TDS_SQLDATACLASSIFICATION;
SensitivityClassification sensitivityClassification = null;
int sensitivityLabelCount = tdsReader.readUnsignedShort();
List<Label> sensitivityLabels = new ArrayList<Label>(sensitivityLabelCount);
for (int i = 0; i < sensitivityLabelCount; i++) {
sensitivityLabels.add(readSensitivityLabel(tdsReader));
}
int informationTypeCount = tdsReader.readUnsignedShort();
List<InformationType> informationTypes = new ArrayList<InformationType>(informationTypeCount);
for (int i = 0; i < informationTypeCount; i++) {
informationTypes.add(readSensitivityInformationType(tdsReader));
}
sensitivityRankSupported = tdsReader
.getServerSupportedDataClassificationVersion() >= TDS.DATA_CLASSIFICATION_VERSION_ADDED_RANK_SUPPORT;
int sensitivityRank = SensitivityRank.NOT_DEFINED.getValue();
if (sensitivityRankSupported) {
sensitivityRank = tdsReader.readInt();
if (!SensitivityRank.isValid(sensitivityRank)) {
tdsReader.throwInvalidTDS();
}
}
int numResultSetColumns = tdsReader.readUnsignedShort();
List<ColumnSensitivity> columnSensitivities = new ArrayList<ColumnSensitivity>(numResultSetColumns);
for (int columnNum = 0; columnNum < numResultSetColumns; columnNum++) {
int numSensitivityProperties = tdsReader.readUnsignedShort();
List<SensitivityProperty> sensitivityProperties = new ArrayList<SensitivityProperty>(
numSensitivityProperties);
for (int sourceNum = 0; sourceNum < numSensitivityProperties; sourceNum++) {
int sensitivityLabelIndex = tdsReader.readUnsignedShort();
Label label = null;
if (sensitivityLabelIndex != Integer.MAX_VALUE) {
if (sensitivityLabelIndex >= sensitivityLabels.size()) {
tdsReader.throwInvalidTDS();
}
label = sensitivityLabels.get(sensitivityLabelIndex);
}
int informationTypeIndex = tdsReader.readUnsignedShort();
InformationType informationType = null;
if (informationTypeIndex != Integer.MAX_VALUE) {
if (informationTypeIndex >= informationTypes.size()) {}
informationType = informationTypes.get(informationTypeIndex);
}
int sensitivityRankProperty = SensitivityRank.NOT_DEFINED.getValue();
if (sensitivityRankSupported) {
sensitivityRankProperty = tdsReader.readInt();
if (!SensitivityRank.isValid(sensitivityRankProperty)) {
tdsReader.throwInvalidTDS();
}
sensitivityProperties.add(new SensitivityProperty(label, informationType, sensitivityRankProperty));
} else {
sensitivityProperties.add(new SensitivityProperty(label, informationType));
}
}
columnSensitivities.add(new ColumnSensitivity(sensitivityProperties));
}
if (sensitivityRankSupported) {
sensitivityClassification = new SensitivityClassification(sensitivityLabels, informationTypes,
columnSensitivities, sensitivityRank);
} else {
sensitivityClassification = new SensitivityClassification(sensitivityLabels, informationTypes,
columnSensitivities);
}
return sensitivityClassification;
}
Column[] buildColumns(StreamColInfo colInfoToken, StreamTabName tabNameToken) throws SQLServerException {
if (null != colInfoToken && null != tabNameToken)
tabNameToken.applyTo(columns, colInfoToken.applyTo(columns));
return columns;
}
}