in source/shared/core_stmt.cpp [687:792]
void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
{
sqlsrv_malloc_auto_ptr<unsigned char> dcbuf;
DWORD dcVersion = 0;
SQLINTEGER dclen = 0, dcIRD = 0;
SQLINTEGER dclenout = 0;
SQLHANDLE ird;
SQLRETURN r;
try {
if (!stmt->data_classification) {
return;
}
if (stmt->current_sensitivity_metadata) {
// Already cached, so return
return;
}
CHECK_CUSTOM_ERROR(!stmt->executed, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_PRE_EXECUTION) {
throw core::CoreException();
}
// Reference: https://docs.microsoft.com/sql/connect/odbc/data-classification
// To retrieve sensitivity classfication data, the first step is to retrieve the IRD(Implementation Row Descriptor) handle by
// calling SQLGetStmtAttr with SQL_ATTR_IMP_ROW_DESC statement attribute
r = ::SQLGetStmtAttr(stmt->handle(), SQL_ATTR_IMP_ROW_DESC, reinterpret_cast<SQLPOINTER*>(&ird), SQL_IS_POINTER, 0);
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in getting Implementation Row Descriptor handle." );
throw core::CoreException();
}
// First call to get dclen
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION, reinterpret_cast<SQLPOINTER>(dcbuf.get()), 0, &dclen);
if (r != SQL_SUCCESS || dclen == 0) {
// log the error first
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in calling SQLGetDescFieldW first time." );
// If this fails, check if it is the "Invalid Descriptor Field error"
SQLRETURN rc;
SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = {'\0'};
SQLSMALLINT len;
rc = ::SQLGetDiagField(SQL_HANDLE_DESC, ird, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len);
CHECK_SQL_ERROR_OR_WARNING(rc, stmt) {
throw core::CoreException();
}
CHECK_CUSTOM_ERROR(!strcmp("HY091", reinterpret_cast<char*>(state)), stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_NOT_AVAILABLE) {
throw core::CoreException();
}
CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "Check if ODBC driver or the server supports the Data Classification feature.") {
throw core::CoreException();
}
}
// Call again to read SQL_CA_SS_DATA_CLASSIFICATION data
dcbuf = static_cast<unsigned char*>(sqlsrv_malloc(dclen * sizeof(char)));
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION, reinterpret_cast<SQLPOINTER>(dcbuf.get()), dclen, &dclenout);
if (r != SQL_SUCCESS) {
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in calling SQLGetDescFieldW again." );
CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "SQLGetDescFieldW failed unexpectedly") {
throw core::CoreException();
}
}
// Start parsing the data (blob)
using namespace data_classification;
// If make it this far, must be using ODBC 17.2 or above. Prior to ODBC 17.4, checking Data Classification version will fail.
// When the function is successful and the version is right, rank info is available for retrieval
bool getRankInfo = false;
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION_VERSION, reinterpret_cast<SQLPOINTER>(&dcVersion), SQL_IS_INTEGER, &dcIRD);
if (r == SQL_SUCCESS && dcVersion >= VERSION_RANK_AVAILABLE) {
getRankInfo = true;
}
// Start parsing the data (blob)
unsigned char *dcptr = dcbuf;
sqlsrv_malloc_auto_ptr<sensitivity_metadata> sensitivity_meta;
sensitivity_meta = new (sqlsrv_malloc(sizeof(sensitivity_metadata))) sensitivity_metadata();
// Parse the name id pairs for labels first then info types
parse_sensitivity_name_id_pairs(stmt, sensitivity_meta->num_labels, &sensitivity_meta->labels, &dcptr);
parse_sensitivity_name_id_pairs(stmt, sensitivity_meta->num_infotypes, &sensitivity_meta->infotypes, &dcptr);
// Next parse the sensitivity properties
parse_column_sensitivity_props(sensitivity_meta, &dcptr, getRankInfo);
unsigned char *dcend = dcbuf;
dcend += dclen;
CHECK_CUSTOM_ERROR(dcptr != dcend, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "Metadata parsing ends unexpectedly") {
throw core::CoreException();
}
stmt->current_sensitivity_metadata = sensitivity_meta;
sensitivity_meta.transferred();
} catch (core::CoreException& e) {
throw e;
}
}