Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed memory issues with data classification #985

Merged
merged 6 commits into from
May 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildscripts/buildtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def generate_build_options(self):
else: # pdo_sqlsrv
cmd_line = ' --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line

cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-embed' + cmd_line
cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-json --enable-embed' + cmd_line
if self.thread == 'nts':
cmd_line = cmd_line + ' --disable-zts'
return cmd_line
Expand Down
2 changes: 1 addition & 1 deletion source/shared/core_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1436,7 +1436,7 @@ namespace data_classification {
struct sensitivity_metadata;

void name_id_pair_free(name_id_pair * pair);
void parse_sensitivity_name_id_pairs(_Inout_ sqlsrv_stmt* stmt, _Inout_ USHORT& numpairs, _Inout_ std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>>& pairs, _Inout_ unsigned char **pptr TSRMLS_CC);
void parse_sensitivity_name_id_pairs(_Inout_ sqlsrv_stmt* stmt, _Inout_ USHORT& numpairs, _Inout_ std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>>* pairs, _Inout_ unsigned char **pptr TSRMLS_CC);
void parse_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr);
USHORT fill_column_sensitivity_array(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Inout_ zval *column_data TSRMLS_CC);

Expand Down
9 changes: 4 additions & 5 deletions source/shared/core_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,7 @@ void sqlsrv_stmt::clean_up_sensitivity_metadata()
{
if (current_sensitivity_metadata) {
current_sensitivity_metadata->~sensitivity_metadata();
sqlsrv_free(current_sensitivity_metadata);
current_sensitivity_metadata = NULL;
current_sensitivity_metadata.reset();
}
}

Expand Down Expand Up @@ -968,7 +967,7 @@ field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQL
}
}

// Set the field name lenth
// Set the field name length
meta_data->field_name_len = static_cast<SQLSMALLINT>( field_name_len );

field_meta_data* result_field_meta_data = meta_data;
Expand Down Expand Up @@ -1052,8 +1051,8 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
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);
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);
Expand Down
22 changes: 15 additions & 7 deletions source/shared/core_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,12 +426,18 @@ namespace data_classification {
void convert_sensivity_field(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_ENCODING encoding, _In_ unsigned char *ptr, _In_ int len, _Inout_updates_bytes_(cchOutLen) char** field_name)
{
sqlsrv_malloc_auto_ptr<SQLWCHAR> temp_field_name;
int temp_field_len = len * 2;
int temp_field_len = len * sizeof(SQLWCHAR);
SQLLEN field_name_len = 0;

if (len == 0) {
*field_name = reinterpret_cast<char*>(sqlsrv_malloc(1));
*field_name[0] = '\0';
return;
}

temp_field_name = static_cast<SQLWCHAR*>(sqlsrv_malloc((len + 1) * sizeof(SQLWCHAR)));
memset(temp_field_name, L'\0', len + 1);
memcpy_s(temp_field_name, temp_field_len, ptr, temp_field_len);
temp_field_name[temp_field_len] = '\0';

bool converted = convert_string_from_utf16(encoding, temp_field_name, len, field_name, field_name_len);

Expand All @@ -450,14 +456,16 @@ namespace data_classification {
}
sqlsrv_free(pair);
}

void parse_sensitivity_name_id_pairs(_Inout_ sqlsrv_stmt* stmt, _Inout_ USHORT& numpairs, _Inout_ std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>>& pairs, _Inout_ unsigned char **pptr)
void parse_sensitivity_name_id_pairs(_Inout_ sqlsrv_stmt* stmt, _Inout_ USHORT& numpairs, _Inout_ std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>>* pairs, _Inout_ unsigned char **pptr)
{
unsigned char *ptr = *pptr;
unsigned short npairs;
numpairs = npairs = *(unsigned short*)ptr;
numpairs = npairs = *(reinterpret_cast<unsigned short*>(ptr));
SQLSRV_ENCODING encoding = ((stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : stmt->encoding());

pairs->reserve(numpairs);

ptr += sizeof(unsigned short);
while (npairs--) {
int namelen, idlen;
Expand Down Expand Up @@ -485,7 +493,7 @@ namespace data_classification {
convert_sensivity_field(stmt, encoding, idptr, idlen, (char**)&id);
pair->id = id;

pairs.push_back(pair.get());
pairs->push_back(pair.get());
pair.transferred();
}
*pptr = ptr;
Expand Down Expand Up @@ -537,7 +545,7 @@ namespace data_classification {
if (meta == NULL) {
return 0;
}

SQLSRV_ASSERT(colno >= 0 && colno < meta->num_columns, "fill_column_sensitivity_array: column number out of bounds");

zval data_classification;
Expand Down
2 changes: 1 addition & 1 deletion source/sqlsrv/stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ PHP_FUNCTION( sqlsrv_field_metadata )
TSRMLS_CC );

if (stmt->data_classification) {
data_classification::fill_column_sensitivity_array(stmt, f, &field_array);
data_classification::fill_column_sensitivity_array(stmt, f, &field_array TSRMLS_CC);
}

// add this field's meta data to the result set meta data
Expand Down
49 changes: 47 additions & 2 deletions test/functional/pdo_sqlsrv/pdo_data_classification.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ PHPT_EXEC=true
require_once('MsSetup.inc');
require_once('MsCommon_mid-refactor.inc');

$dataClassKey = 'Data Classification';

function testConnAttrCases()
{
// Attribute PDO::SQLSRV_ATTR_DATA_CLASSIFICATION is limited to statement level only
Expand Down Expand Up @@ -157,8 +159,10 @@ function verifyClassInfo($input, $actual)

function compareDataClassification($stmt1, $stmt2, $classData)
{
global $dataClassKey;

$numCol = $stmt1->columnCount();
$noClassInfo = array('Data Classification' => array());
$noClassInfo = array($dataClassKey => array());

for ($i = 0; $i < $numCol; $i++) {
$metadata1 = $stmt1->getColumnMeta($i);
Expand All @@ -176,7 +180,7 @@ function compareDataClassification($stmt1, $stmt2, $classData)
}
} else {
// Verify the classification metadata
if (!verifyClassInfo($classData[$i], $value['Data Classification'])) {
if (!verifyClassInfo($classData[$i], $value[$dataClassKey])) {
var_dump($value);
}
}
Expand All @@ -190,6 +194,45 @@ function compareDataClassification($stmt1, $stmt2, $classData)
}
}

function runBatchQuery($conn, $tableName)
{
global $dataClassKey;

$options = array(PDO::SQLSRV_ATTR_DATA_CLASSIFICATION => true);
$tsql = "SELECT SSN, BirthDate FROM $tableName";

// Run a batch query
$batchQuery = $tsql . ';' . $tsql;
$stmt = $conn->prepare($batchQuery, $options);
$stmt->execute();

$numCol = $stmt->columnCount();

// The metadata returned should be the same
$c = rand(0, $numCol - 1);
$metadata1 = $stmt->getColumnMeta($c);
$stmt->nextRowset();
$metadata2 = $stmt->getColumnMeta($c);

// Check the returned flags
$data1 = $metadata1['flags'];
$data2 = $metadata2['flags'];

if (!array_key_exists($dataClassKey, $data1) || !array_key_exists($dataClassKey, $data2)) {
echo "Metadata returned with no classification data\n";
var_dump($data1);
var_dump($data2);
} else {
$jstr1 = json_encode($data1[$dataClassKey]);
$jstr2 = json_encode($data2[$dataClassKey]);
if ($jstr1 !== $jstr2) {
echo "The JSON encoded strings should be identical\n";
var_dump($jstr1);
var_dump($jstr2);
}
}
}

///////////////////////////////////////////////////////////////////////////////////////
try {
testConnAttrCases();
Expand Down Expand Up @@ -254,6 +297,8 @@ try {

unset($stmt1);
unset($stmt2);

runBatchQuery($conn, $tableName);
}

dropTable($conn, $tableName);
Expand Down
50 changes: 47 additions & 3 deletions test/functional/sqlsrv/sqlsrv_data_classification.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--FILE--
<?php
<?php
$dataClassKey = 'Data Classification';

function testErrorCases($conn, $tableName, $isSupported)
{
// This function will check two error cases:
Expand Down Expand Up @@ -136,6 +138,8 @@ function verifyClassInfo($input, $actual)

function compareDataClassification($stmt1, $stmt2, $classData)
{
global $dataClassKey;

$numCol = sqlsrv_num_fields($stmt1);

$metadata1 = sqlsrv_field_metadata($stmt1);
Expand All @@ -152,7 +156,7 @@ function compareDataClassification($stmt1, $stmt2, $classData)
// empty array. Otherwise, it should contain an array with one set of
// Label (name, id) and Information Type (name, id)

$noClassInfo = array('Data Classification' => array());
$noClassInfo = array($dataClassKey => array());
for ($i = 0; $i < $numCol; $i++) {
$diff = array_diff_assoc($metadata2[$i], $metadata1[$i]);

Expand All @@ -164,13 +168,51 @@ function compareDataClassification($stmt1, $stmt2, $classData)
}
} else {
// Verify the classification metadata
if (!verifyClassInfo($classData[$i], $diff['Data Classification'])) {
if (!verifyClassInfo($classData[$i], $diff[$dataClassKey])) {
var_dump($diff);
}
}
}
}

function runBatchQuery($conn, $tableName)
{
global $dataClassKey;

$options = array('DataClassification' => true);
$tsql = "SELECT SSN, BirthDate FROM $tableName";
$batchQuery = $tsql . ';' . $tsql;

$stmt = sqlsrv_query($conn, $batchQuery, array(), $options);
if (!$stmt) {
fatalError("Error when calling sqlsrv_query '$tsql'.\n");
}

$numCol = sqlsrv_num_fields($stmt);
$c = rand(0, $numCol - 1);

$metadata1 = sqlsrv_field_metadata($stmt);
if (!$metadata1 || !array_key_exists($dataClassKey, $metadata1[$c])) {
fatalError("runBatchQuery(1): failed to get metadata");
}
$result = sqlsrv_next_result($stmt);
if (is_null($result) || !$result) {
fatalError("runBatchQuery: failed to get next result");
}
$metadata2 = sqlsrv_field_metadata($stmt);
if (!$metadata2 || !array_key_exists($dataClassKey, $metadata2[$c])) {
fatalError("runBatchQuery(2): failed to get metadata");
}

$jstr1 = json_encode($metadata1[$c][$dataClassKey]);
$jstr2 = json_encode($metadata2[$c][$dataClassKey]);
if ($jstr1 !== $jstr2) {
echo "The JSON encoded strings should be identical\n";
var_dump($jstr1);
var_dump($jstr2);
}
}

///////////////////////////////////////////////////////////////////////////////////////
require_once('MsCommon.inc');

Expand Down Expand Up @@ -248,6 +290,8 @@ if ($isSupported) {

compareDataClassification($stmt, $stmt2, $classData);
sqlsrv_free_stmt($stmt2);

runBatchQuery($conn, $tableName);
}

sqlsrv_free_stmt($stmt);
Expand Down