com.microsoft.sqlserver.jdbc.StreamColumns Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mssql-jdbc Show documentation
Show all versions of mssql-jdbc Show documentation
Microsoft JDBC Driver for SQL Server.
/*
* Microsoft JDBC Driver for SQL Server
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
package com.microsoft.sqlserver.jdbc;
/**
* StreamColumns stores the column meta data for a result set. StreamColumns parses the inbound TDS packet stream to determine column meta data.
*/
final class StreamColumns extends StreamPacket {
/** the set of columns */
private Column[] columns;
/* The CekTable. */
private CekTable cekTable = null;
private boolean shouldHonorAEForRead = false;
/* Gets the CekTable */
CekTable getCekTable() {
return cekTable;
}
/**
* Create a new stream columns
*/
/* L0 */ StreamColumns() {
super(TDS.TDS_COLMETADATA);
}
/**
* Create a new stream columns with column encryption setting
*/
StreamColumns(boolean honorAE) {
super(TDS.TDS_COLMETADATA);
shouldHonorAEForRead = honorAE;
}
/**
* Parse a result set column meta data TDS stream for CEK table entry.
*
* @throws SQLServerException
*/
CekTableEntry readCEKTableEntry(TDSReader tdsReader) throws SQLServerException {
// Read the DB ID
int databaseId = tdsReader.readInt();
// Read the keyID
int cekId = tdsReader.readInt();
// Read the key version
int cekVersion = tdsReader.readInt();
// Read the key MD Version
byte[] cekMdVersion = new byte[8];
tdsReader.readBytes(cekMdVersion, 0, 8);
// Read the value count
int cekValueCount = tdsReader.readUnsignedByte();
CekTableEntry cekTableEntry = new CekTableEntry(cekValueCount);
for (int i = 0; i < cekValueCount; i++) {
// Read individual CEK values
short encryptedCEKlength = tdsReader.readShort();
byte[] encryptedCek = new byte[encryptedCEKlength];
// Read the actual encrypted CEK
tdsReader.readBytes(encryptedCek, 0, encryptedCEKlength);
// Read the length of key store name
int keyStoreLength = tdsReader.readUnsignedByte();
// And read the key store name now
String keyStoreName = tdsReader.readUnicodeString(keyStoreLength);
// Read the length of key Path
int keyPathLength = tdsReader.readShort();
// Read the key path string
String keyPath = tdsReader.readUnicodeString(keyPathLength);
// Read the length of the string carrying the encryption algo
int algorithmLength = tdsReader.readUnsignedByte();
// Read the string carrying the encryption algo (eg. RSA_PKCS_OAEP)
String algorithmName = tdsReader.readUnicodeString(algorithmLength);
// Add this encrypted CEK blob to our list of encrypted values for the CEK
cekTableEntry.add(encryptedCek, databaseId, cekId, cekVersion, cekMdVersion, keyPath, keyStoreName, algorithmName);
}
return cekTableEntry;
}
/**
* Parse a result set column meta data TDS stream for CEK table.
*
* @throws SQLServerException
*/
void readCEKTable(TDSReader tdsReader) throws SQLServerException {
// Read count
int tableSize = tdsReader.readShort();
// CEK table will be sent if AE is enabled. If none of the columns are encrypted, the CEK table
// size would be zero.
if (0 != tableSize) {
cekTable = new CekTable(tableSize);
// Read individual entries
for (int i = 0; i < tableSize; i++) {
// SqlTceCipherInfoEntry entry;
cekTable.setCekTableEntry(i, readCEKTableEntry(tdsReader));
}
}
}
/**
* Parse a result set column meta data TDS stream for crypto metadata for AE.
*
* @throws SQLServerException
*/
CryptoMetadata readCryptoMetadata(TDSReader tdsReader) throws SQLServerException {
short ordinal = 0;
// Read the ordinal into cipher table. For return values there is not cipher table and no ordinal.
if (null != cekTable) {
ordinal = tdsReader.readShort();
}
TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, false);
// Read the cipher algorithm Id
byte algorithmId = (byte) tdsReader.readUnsignedByte();
String algorithmName = null;
if ((byte) TDS.CUSTOM_CIPHER_ALGORITHM_ID == algorithmId) {
// Custom encryption algorithm, read the name
int nameSize = tdsReader.readUnsignedByte();
algorithmName = tdsReader.readUnicodeString(nameSize);
}
// Read Encryption Type.
byte encryptionType = (byte) tdsReader.readUnsignedByte();
// Read Normalization Rule Version.
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;
}
/**
* Parse a result set column meta data TDS stream.
*
* @throws SQLServerException
*/
void setFromTDS(TDSReader tdsReader) throws SQLServerException {
if (TDS.TDS_COLMETADATA != tdsReader.readUnsignedByte())
assert false;
int nTotColumns = tdsReader.readUnsignedShort();
// Handle the magic NoMetaData value
if (0xFFFF == nTotColumns)
return;
if (tdsReader.getServerSupportsColumnEncryption()) {
readCEKTable(tdsReader);
}
this.columns = new Column[nTotColumns];
for (int numColumns = 0; numColumns < nTotColumns; numColumns++) {
// Column type info
TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, true);
// Table name
//
// Table name is set at this point for TEXT/NTEXT/IMAGE columns only.
// For other columns, table name may be set via COLUMNINFO and TABNAME tokens.
SQLIdentifier tableName = new SQLIdentifier();
if (SSType.TEXT == typeInfo.getSSType() || SSType.NTEXT == typeInfo.getSSType() || SSType.IMAGE == typeInfo.getSSType()) {
// Yukon and later, table names are returned as multi-part SQL identifiers.
tableName = tdsReader.readSQLIdentifier();
}
CryptoMetadata cryptoMeta = null;
if (tdsReader.getServerSupportsColumnEncryption() && typeInfo.isEncrypted()) {
cryptoMeta = readCryptoMetadata(tdsReader);
cryptoMeta.baseTypeInfo.setFlags(typeInfo.getFlagsAsShort());
typeInfo.setSQLCollation(cryptoMeta.baseTypeInfo.getSQLCollation());
}
// Column name
String columnName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte());
if (shouldHonorAEForRead) {
this.columns[numColumns] = new Column(typeInfo, columnName, tableName, cryptoMeta);
}
else {
// Set null for crypto metadata if column encryption setting is off at the connection or at the statement level.
this.columns[numColumns] = new Column(typeInfo, columnName, tableName, null);
}
}
}
/**
* Applies per-column table information derived from COLINFO and TABNAME tokens to the set of columns defined by this COLMETADATA token to produce
* the complete set of column information.
*/
Column[] buildColumns(StreamColInfo colInfoToken,
StreamTabName tabNameToken) throws SQLServerException {
if (null != colInfoToken && null != tabNameToken)
tabNameToken.applyTo(columns, colInfoToken.applyTo(columns));
return columns;
}
}