2016-04-12 23:43:46 +02:00
//---------------------------------------------------------------------------------------------------------------------------------
// File: core_util.cpp
//
// Contents: Utility functions used by both connection or statement functions for both the PDO and sqlsrv drivers
//
// Comments: Mostly error handling and some type handling
//
2019-01-16 19:19:01 +01:00
// Microsoft Drivers 5.6 for PHP for SQL Server
2016-04-12 23:43:46 +02:00
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
# include "core_sqlsrv.h"
namespace {
// *** internal constants ***
log_callback g_driver_log ;
// internal error that says that FormatMessage failed
SQLCHAR INTERNAL_FORMAT_ERROR [ ] = " An internal error occurred. FormatMessage failed writing an error message. " ;
// buffer used to hold a formatted log message prior to actually logging it.
2018-08-01 02:22:56 +02:00
char last_err_msg [ 2048 ] = { ' \0 ' } ; // 2k to hold the error messages
2016-04-12 23:43:46 +02:00
// routine used by utf16_string_from_mbcs_string
2017-06-22 23:04:34 +02:00
unsigned int convert_string_from_default_encoding ( _In_ unsigned int php_encoding , _In_reads_bytes_ ( mbcs_len ) char const * mbcs_in_string ,
_In_ unsigned int mbcs_len ,
2017-01-21 00:14:46 +01:00
_Out_writes_ ( utf16_len ) __transfer ( mbcs_in_string ) SQLWCHAR * utf16_out_string ,
2019-05-10 22:03:22 +02:00
_In_ unsigned int utf16_len , bool use_strict_conversion = false ) ;
2016-04-12 23:43:46 +02:00
}
// SQLSTATE for all internal errors
SQLCHAR IMSSP [ ] = " IMSSP " ;
// SQLSTATE for all internal warnings
SQLCHAR SSPWARN [ ] = " 01SSP " ;
// write to the php log if the severity and subsystem match the filters currently set in the INI or
// the script (sqlsrv_configure).
2017-06-22 23:04:34 +02:00
void write_to_log ( _In_ unsigned int severity TSRMLS_DC , _In_ const char * msg , . . . )
2016-04-12 23:43:46 +02:00
{
SQLSRV_ASSERT ( ! ( g_driver_log = = NULL ) , " Must register a driver log function. " ) ;
va_list args ;
va_start ( args , msg ) ;
g_driver_log ( severity TSRMLS_CC , msg , & args ) ;
va_end ( args ) ;
}
2017-06-22 23:04:34 +02:00
void core_sqlsrv_register_logger ( _In_ log_callback driver_logger )
2016-04-12 23:43:46 +02:00
{
g_driver_log = driver_logger ;
}
// convert a string from utf-16 to the encoding and return the new string in the pointer parameter and new
// length in the len parameter. If no errors occurred during convertion, true is returned and the original
// utf-16 string is released by this function if no errors occurred. Otherwise the parameters are not changed
// and false is returned.
2017-06-22 23:04:34 +02:00
bool convert_string_from_utf16_inplace ( _In_ SQLSRV_ENCODING encoding , _Inout_updates_z_ ( len ) char * * string , _Inout_ SQLLEN & len )
2016-04-12 23:43:46 +02:00
{
SQLSRV_ASSERT ( string ! = NULL , " String must be specified " ) ;
if ( validate_string ( * string , len ) ) {
return true ;
}
char * outString = NULL ;
SQLLEN outLen = 0 ;
2017-01-21 00:14:46 +01:00
bool result = convert_string_from_utf16 ( encoding , reinterpret_cast < const SQLWCHAR * > ( * string ) , int ( len / sizeof ( SQLWCHAR ) ) , & outString , outLen ) ;
2016-04-12 23:43:46 +02:00
if ( result )
{
sqlsrv_free ( * string ) ;
* string = outString ;
len = outLen ;
}
return result ;
}
2017-06-22 23:04:34 +02:00
bool validate_string ( _In_ char * string , _In_ SQLLEN & len )
2016-04-12 23:43:46 +02:00
{
2017-01-27 00:14:30 +01:00
SQLSRV_ASSERT ( string ! = NULL , " String must be specified " ) ;
2016-04-12 23:43:46 +02:00
2017-01-27 00:14:30 +01:00
//for the empty string, we simply returned we converted it
if ( len = = 0 & & string [ 0 ] = = ' \0 ' ) {
return true ;
}
if ( ( len / sizeof ( SQLWCHAR ) ) > INT_MAX ) {
2017-01-21 00:14:46 +01:00
LOG ( SEV_ERROR , " UTP-16 (wide character) string mapping: buffer length exceeded. " ) ;
throw core : : CoreException ( ) ;
}
2017-01-27 00:14:30 +01:00
return false ;
2016-04-12 23:43:46 +02:00
}
2017-06-22 23:04:34 +02:00
bool convert_string_from_utf16 ( _In_ SQLSRV_ENCODING encoding , _In_reads_bytes_ ( cchInLen ) const SQLWCHAR * inString , _In_ SQLINTEGER cchInLen , _Inout_updates_bytes_ ( cchOutLen ) char * * outString , _Out_ SQLLEN & cchOutLen )
2016-04-12 23:43:46 +02:00
{
SQLSRV_ASSERT ( inString ! = NULL , " Input string must be specified " ) ;
SQLSRV_ASSERT ( outString ! = NULL , " Output buffer pointer must be specified " ) ;
SQLSRV_ASSERT ( * outString = = NULL , " Output buffer pointer must not be set " ) ;
if ( cchInLen = = 0 & & inString [ 0 ] = = L ' \0 ' ) {
* outString = reinterpret_cast < char * > ( sqlsrv_malloc ( 1 ) ) ;
* outString [ 0 ] = ' \0 ' ;
cchOutLen = 0 ;
return true ;
}
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
DWORD flags = 0 ;
2017-06-29 21:23:46 +02:00
if ( encoding = = CP_UTF8 & & isVistaOrGreater ) {
2016-04-12 23:43:46 +02:00
// Vista (and later) will detect invalid UTF-16 characters and raise an error.
flags = WC_ERR_INVALID_CHARS ;
}
2017-01-27 02:06:17 +01:00
# ifndef _WIN32
2018-12-04 22:00:34 +01:00
// Allocate enough space to hold the largest possible number of bytes for UTF-8 conversion
// instead of calling FromUtf16, for performance reasons
cchOutLen = 4 * cchInLen ;
2017-01-21 00:14:46 +01:00
# else
2018-12-04 22:00:34 +01:00
// Calculate the number of output bytes required - no performance hit here because
// WideCharToMultiByte is highly optimised
2016-04-12 23:43:46 +02:00
cchOutLen = WideCharToMultiByte ( encoding , flags ,
inString , cchInLen ,
NULL , 0 , NULL , NULL ) ;
2017-01-27 02:06:17 +01:00
# endif // !_WIN32
2017-01-21 00:14:46 +01:00
2016-04-12 23:43:46 +02:00
if ( cchOutLen = = 0 ) {
return false ;
}
// Create a buffer to fit the encoded string
char * newString = reinterpret_cast < char * > ( sqlsrv_malloc ( cchOutLen + 1 /* NULL char*/ ) ) ;
2018-12-04 22:00:34 +01:00
memset ( newString , ' \0 ' , cchOutLen + 1 ) ;
2017-01-21 00:14:46 +01:00
2017-01-27 02:06:17 +01:00
# ifndef _WIN32
2018-12-04 22:00:34 +01:00
int rc = SystemLocale : : FromUtf16Strict ( encoding , inString , cchInLen , newString , static_cast < int > ( cchOutLen ) ) ;
2017-01-21 00:14:46 +01:00
# else
2017-01-27 02:06:17 +01:00
int rc = WideCharToMultiByte ( encoding , flags , inString , cchInLen , newString , static_cast < int > ( cchOutLen ) , NULL , NULL ) ;
# endif // !_WIN32
2016-04-12 23:43:46 +02:00
if ( rc = = 0 ) {
cchOutLen = 0 ;
sqlsrv_free ( newString ) ;
return false ;
}
2018-12-04 22:00:34 +01:00
char * newString2 = reinterpret_cast < char * > ( sqlsrv_malloc ( rc + 1 /* NULL char*/ ) ) ;
memset ( newString2 , ' \0 ' , rc + 1 ) ;
memcpy_s ( newString2 , rc , newString , rc ) ;
sqlsrv_free ( newString ) ;
2016-04-12 23:43:46 +02:00
2018-12-04 22:00:34 +01:00
* outString = newString2 ;
cchOutLen = rc ;
2016-04-12 23:43:46 +02:00
return true ;
}
// thin wrapper around convert_string_from_default_encoding that handles
// allocation of the destination string. An empty string passed in returns
// failure since it's a failure case for convert_string_from_default_encoding.
2017-06-22 23:04:34 +02:00
SQLWCHAR * utf16_string_from_mbcs_string ( _In_ SQLSRV_ENCODING php_encoding , _In_reads_bytes_ ( mbcs_len ) const char * mbcs_string , _In_ unsigned int mbcs_len ,
2019-05-10 22:03:22 +02:00
_Out_ unsigned int * utf16_len , bool use_strict_conversion )
2016-04-12 23:43:46 +02:00
{
* utf16_len = ( mbcs_len + 1 ) ;
2017-01-21 00:14:46 +01:00
SQLWCHAR * utf16_string = reinterpret_cast < SQLWCHAR * > ( sqlsrv_malloc ( * utf16_len * sizeof ( SQLWCHAR ) ) ) ;
2019-05-10 22:03:22 +02:00
* utf16_len = convert_string_from_default_encoding ( php_encoding , mbcs_string , mbcs_len , utf16_string , * utf16_len , use_strict_conversion ) ;
2017-01-21 00:14:46 +01:00
2016-04-12 23:43:46 +02:00
if ( * utf16_len = = 0 ) {
// we preserve the error and reset it because sqlsrv_free resets the last error
DWORD last_error = GetLastError ( ) ;
sqlsrv_free ( utf16_string ) ;
SetLastError ( last_error ) ;
return NULL ;
}
return utf16_string ;
}
// call to retrieve an error from ODBC. This uses SQLGetDiagRec, so the
// errno is 1 based. It returns it as an array with 3 members:
// 1/SQLSTATE) sqlstate
// 2/code) driver specific error code
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
2017-06-22 23:04:34 +02:00
bool core_sqlsrv_get_odbc_error ( _Inout_ sqlsrv_context & ctx , _In_ int record_number , _Inout_ sqlsrv_error_auto_ptr & error , _In_ logging_severity severity
2016-04-12 23:43:46 +02:00
TSRMLS_DC )
{
SQLHANDLE h = ctx . handle ( ) ;
SQLSMALLINT h_type = ctx . handle_type ( ) ;
if ( h = = NULL ) {
return false ;
}
SQLRETURN r = SQL_SUCCESS ;
SQLSMALLINT wmessage_len = 0 ;
2018-08-01 02:22:56 +02:00
SQLWCHAR wsqlstate [ SQL_SQLSTATE_BUFSIZE ] = { L ' \0 ' } ;
SQLWCHAR wnative_message [ SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ] = { L ' \0 ' } ;
2016-04-12 23:43:46 +02:00
SQLSRV_ENCODING enc = ctx . encoding ( ) ;
switch ( h_type ) {
case SQL_HANDLE_STMT :
{
sqlsrv_stmt * stmt = static_cast < sqlsrv_stmt * > ( & ctx ) ;
if ( stmt - > current_results ! = NULL ) {
error = stmt - > current_results - > get_diag_rec ( record_number ) ;
// don't use the CHECK* macros here since it will trigger reentry into the error handling system
2017-01-21 00:14:46 +01:00
if ( error = = 0 ) {
2016-04-12 23:43:46 +02:00
return false ;
}
break ;
}
// convert the error into the encoding of the context
if ( enc = = SQLSRV_ENCODING_DEFAULT ) {
enc = stmt - > conn - > encoding ( ) ;
}
}
default :
error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ) ;
r = SQLGetDiagRecW ( h_type , h , record_number , wsqlstate , & error - > native_code , wnative_message ,
2017-09-12 18:42:13 +02:00
SQL_MAX_ERROR_MESSAGE_LENGTH + 1 , & wmessage_len ) ;
2016-04-12 23:43:46 +02:00
// don't use the CHECK* macros here since it will trigger reentry into the error handling system
2017-03-27 23:07:43 +02:00
// Workaround for a bug in unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV).
// Instead of returning false, we return an empty error message to prevent the driver from throwing an exception.
// To reproduce:
// Create a connection and close it (return it to the pool)
// Create a new connection from the pool.
// Prepare and execute a statement that generates an info message (such as 'USE tempdb;')
# ifdef __APPLE__
if ( r = = SQL_NO_DATA & & ctx . driver ( ) ! = NULL /*PDO SQLSRV*/ ) {
r = SQL_SUCCESS ;
}
# endif // __APPLE__
2016-04-12 23:43:46 +02:00
if ( ! SQL_SUCCEEDED ( r ) | | r = = SQL_NO_DATA ) {
return false ;
}
2017-02-02 23:25:09 +01:00
// We need to calculate number of characters
2017-12-13 22:33:32 +01:00
SQLINTEGER wsqlstate_len = sizeof ( wsqlstate ) / sizeof ( SQLWCHAR ) ;
2016-04-12 23:43:46 +02:00
SQLLEN sqlstate_len = 0 ;
2018-08-20 23:51:33 +02:00
convert_string_from_utf16 ( enc , wsqlstate , wsqlstate_len , ( char * * ) & error - > sqlstate , sqlstate_len ) ;
2016-04-12 23:43:46 +02:00
SQLLEN message_len = 0 ;
2018-08-20 23:51:33 +02:00
if ( r = = SQL_SUCCESS_WITH_INFO & & wmessage_len > SQL_MAX_ERROR_MESSAGE_LENGTH ) {
// note that wmessage_len is the number of characters required for the error message --
// create a new buffer big enough for this lengthy error message
sqlsrv_malloc_auto_ptr < SQLWCHAR > wnative_message_str ;
SQLSMALLINT expected_len = wmessage_len * sizeof ( SQLWCHAR ) ;
SQLSMALLINT returned_len = 0 ;
wnative_message_str = reinterpret_cast < SQLWCHAR * > ( sqlsrv_malloc ( expected_len ) ) ;
memset ( wnative_message_str , ' \0 ' , expected_len ) ;
SQLRETURN rtemp = : : SQLGetDiagFieldW ( h_type , h , record_number , SQL_DIAG_MESSAGE_TEXT , wnative_message_str , wmessage_len , & returned_len ) ;
if ( ! SQL_SUCCEEDED ( rtemp ) | | returned_len ! = expected_len ) {
// something went wrong
return false ;
}
convert_string_from_utf16 ( enc , wnative_message_str , wmessage_len , ( char * * ) & error - > native_message , message_len ) ;
} else {
convert_string_from_utf16 ( enc , wnative_message , wmessage_len , ( char * * ) & error - > native_message , message_len ) ;
}
if ( message_len = = 0 & & error - > native_message = = NULL ) {
// something went wrong
return false ;
}
2016-04-12 23:43:46 +02:00
break ;
}
// log the error first
LOG ( severity , " %1!s!: SQLSTATE = %2!s! " , ctx . func ( ) , error - > sqlstate ) ;
LOG ( severity , " %1!s!: error code = %2!d! " , ctx . func ( ) , error - > native_code ) ;
LOG ( severity , " %1!s!: message = %2!s! " , ctx . func ( ) , error - > native_message ) ;
error - > format = false ;
return true ;
}
// format and return a driver specfic error
2017-06-22 23:04:34 +02:00
void core_sqlsrv_format_driver_error ( _In_ sqlsrv_context & ctx , _In_ sqlsrv_error_const const * custom_error ,
_Out_ sqlsrv_error_auto_ptr & formatted_error , _In_ logging_severity severity TSRMLS_DC , _In_opt_ va_list * args )
2016-04-12 23:43:46 +02:00
{
// allocate space for the formatted message
formatted_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ) ;
formatted_error - > sqlstate = reinterpret_cast < SQLCHAR * > ( sqlsrv_malloc ( SQL_SQLSTATE_BUFSIZE ) ) ;
2017-09-06 01:51:40 +02:00
formatted_error - > native_message = reinterpret_cast < SQLCHAR * > ( sqlsrv_malloc ( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ) ) ;
2016-04-12 23:43:46 +02:00
DWORD rc = FormatMessage ( FORMAT_MESSAGE_FROM_STRING , reinterpret_cast < LPSTR > ( custom_error - > native_message ) , 0 , 0 ,
2017-09-06 01:51:40 +02:00
reinterpret_cast < LPSTR > ( formatted_error - > native_message ) , SQL_MAX_ERROR_MESSAGE_LENGTH , args ) ;
2016-04-12 23:43:46 +02:00
if ( rc = = 0 ) {
2017-09-06 01:51:40 +02:00
strcpy_s ( reinterpret_cast < char * > ( formatted_error - > native_message ) , SQL_MAX_ERROR_MESSAGE_LENGTH ,
2016-04-12 23:43:46 +02:00
reinterpret_cast < char * > ( INTERNAL_FORMAT_ERROR ) ) ;
}
strcpy_s ( reinterpret_cast < char * > ( formatted_error - > sqlstate ) , SQL_SQLSTATE_BUFSIZE ,
reinterpret_cast < char * > ( custom_error - > sqlstate ) ) ;
formatted_error - > native_code = custom_error - > native_code ;
// log the error
LOG ( severity , " %1!s!: SQLSTATE = %2!s! " , ctx . func ( ) , formatted_error - > sqlstate ) ;
LOG ( severity , " %1!s!: error code = %2!d! " , ctx . func ( ) , formatted_error - > native_code ) ;
LOG ( severity , " %1!s!: message = %2!s! " , ctx . func ( ) , formatted_error - > native_message ) ;
}
2017-06-22 23:04:34 +02:00
DWORD core_sqlsrv_format_message ( _Out_ char * output_buffer , _In_ unsigned output_len , _In_opt_ const char * format , . . . )
2016-04-12 23:43:46 +02:00
{
va_list format_args ;
va_start ( format_args , format ) ;
2017-01-27 00:14:30 +01:00
DWORD rc = FormatMessage ( FORMAT_MESSAGE_FROM_STRING , format , 0 , 0 , static_cast < LPSTR > ( output_buffer ) , output_len , & format_args ) ;
2016-04-12 23:43:46 +02:00
va_end ( format_args ) ;
return rc ;
}
// return an error message for GetLastError using FormatMessage.
// this function returns the msg pointer so that it may be used within
// another function call such as handle_error
2017-06-22 23:04:34 +02:00
const char * get_last_error_message ( _Inout_ DWORD last_error )
2016-04-12 23:43:46 +02:00
{
if ( last_error = = 0 ) {
last_error = GetLastError ( ) ;
}
DWORD r = FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM , NULL , last_error , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) ,
last_err_msg , sizeof ( last_err_msg ) , NULL ) ;
if ( r = = 0 ) {
SQLSRV_STATIC_ASSERT ( sizeof ( INTERNAL_FORMAT_ERROR ) < sizeof ( last_err_msg ) ) ;
std : : copy ( INTERNAL_FORMAT_ERROR , INTERNAL_FORMAT_ERROR + sizeof ( INTERNAL_FORMAT_ERROR ) , last_err_msg ) ;
}
return last_err_msg ;
}
// die
// Terminate the PHP request with an error message
// We use this function rather than php_error directly because we use the FormatMessage syntax in most other
// places within the extension (e.g., LOG), whereas php_error uses the printf format syntax. There were
// places where we were using the FormatMessage syntax inadvertently with DIE which left messages without
// proper information. Rather than convert those messages and try and remember the difference between LOG and
// DIE, it is simpler to make the format syntax common between them.
2017-06-22 23:04:34 +02:00
void die ( _In_opt_ const char * msg , . . . )
2016-04-12 23:43:46 +02:00
{
va_list format_args ;
va_start ( format_args , msg ) ;
2017-01-27 00:14:30 +01:00
DWORD rc = FormatMessage ( FORMAT_MESSAGE_FROM_STRING , msg , 0 , 0 , last_err_msg , sizeof ( last_err_msg ) , & format_args ) ;
2016-04-12 23:43:46 +02:00
va_end ( format_args ) ;
2018-07-27 00:21:03 +02:00
if ( rc = = 0 ) {
php_error ( E_ERROR , " %s " , reinterpret_cast < const char * > ( INTERNAL_FORMAT_ERROR ) ) ;
2016-04-12 23:43:46 +02:00
}
2018-07-27 00:21:03 +02:00
php_error ( E_ERROR , " %s " , last_err_msg ) ;
2016-04-12 23:43:46 +02:00
}
namespace {
// convert from the default encoding specified by the "CharacterSet"
// connection option to UTF-16. mbcs_len and utf16_len are sizes in
// bytes. The return is the number of UTF-16 characters in the string
// returned in utf16_out_string. An empty string passed in will result as
// a failure since MBTWC returns 0 for both an empty string and failure
// to convert.
2017-06-22 23:04:34 +02:00
unsigned int convert_string_from_default_encoding ( _In_ unsigned int php_encoding , _In_reads_bytes_ ( mbcs_len ) char const * mbcs_in_string ,
_In_ unsigned int mbcs_len , _Out_writes_ ( utf16_len ) __transfer ( mbcs_in_string ) SQLWCHAR * utf16_out_string ,
2019-05-10 22:03:22 +02:00
_In_ unsigned int utf16_len , bool use_strict_conversion )
2016-04-12 23:43:46 +02:00
{
unsigned int win_encoding = CP_ACP ;
switch ( php_encoding ) {
case SQLSRV_ENCODING_CHAR :
win_encoding = CP_ACP ;
break ;
// this shouldn't ever be set
case SQLSRV_ENCODING_BINARY :
DIE ( " Invalid encoding. " ) ;
break ;
default :
win_encoding = php_encoding ;
break ;
}
2019-05-10 01:56:18 +02:00
# ifndef _WIN32
unsigned int required_len ;
2019-05-10 22:03:22 +02:00
if ( use_strict_conversion ) {
2019-05-10 01:56:18 +02:00
required_len = SystemLocale : : ToUtf16Strict ( win_encoding , mbcs_in_string , mbcs_len , utf16_out_string , utf16_len ) ;
}
else {
required_len = SystemLocale : : ToUtf16 ( win_encoding , mbcs_in_string , mbcs_len , utf16_out_string , utf16_len ) ;
}
2017-01-21 00:14:46 +01:00
# else
2017-01-27 00:14:30 +01:00
unsigned int required_len = MultiByteToWideChar ( win_encoding , MB_ERR_INVALID_CHARS , mbcs_in_string , mbcs_len , utf16_out_string , utf16_len ) ;
2017-01-27 02:06:17 +01:00
# endif // !_Win32
2017-01-21 00:14:46 +01:00
2016-04-12 23:43:46 +02:00
if ( required_len = = 0 ) {
return 0 ;
}
2018-08-01 02:22:56 +02:00
utf16_out_string [ required_len ] = ' \0 ' ;
2016-04-12 23:43:46 +02:00
return required_len ;
}
}
2019-05-01 17:03:33 +02:00
namespace data_classification {
const char * DATA_CLASS = " Data Classification " ;
const char * LABEL = " Label " ;
const char * INFOTYPE = " Information Type " ;
const char * NAME = " name " ;
const char * ID = " id " ;
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 ;
2019-05-09 00:18:15 +02:00
int temp_field_len = len * sizeof ( SQLWCHAR ) ;
2019-05-01 17:03:33 +02:00
SQLLEN field_name_len = 0 ;
2019-05-09 00:18:15 +02:00
if ( len = = 0 ) {
* field_name = reinterpret_cast < char * > ( sqlsrv_malloc ( 1 ) ) ;
* field_name [ 0 ] = ' \0 ' ;
return ;
}
2019-05-01 17:03:33 +02:00
temp_field_name = static_cast < SQLWCHAR * > ( sqlsrv_malloc ( ( len + 1 ) * sizeof ( SQLWCHAR ) ) ) ;
2019-05-09 00:18:15 +02:00
memset ( temp_field_name , L ' \0 ' , len + 1 ) ;
2019-05-01 17:03:33 +02:00
memcpy_s ( temp_field_name , temp_field_len , ptr , temp_field_len ) ;
bool converted = convert_string_from_utf16 ( encoding , temp_field_name , len , field_name , field_name_len ) ;
CHECK_CUSTOM_ERROR ( ! converted , stmt , SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE , get_last_error_message ( ) ) {
throw core : : CoreException ( ) ;
}
}
void name_id_pair_free ( _Inout_ name_id_pair * pair )
{
if ( pair - > name ) {
pair - > name . reset ( ) ;
}
if ( pair - > id ) {
pair - > id . reset ( ) ;
}
sqlsrv_free ( pair ) ;
}
2019-05-09 00:18:15 +02:00
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 )
2019-05-01 17:03:33 +02:00
{
unsigned char * ptr = * pptr ;
unsigned short npairs ;
2019-05-09 00:18:15 +02:00
numpairs = npairs = * ( reinterpret_cast < unsigned short * > ( ptr ) ) ;
2019-05-01 17:03:33 +02:00
SQLSRV_ENCODING encoding = ( ( stmt - > encoding ( ) = = SQLSRV_ENCODING_DEFAULT ) ? stmt - > conn - > encoding ( ) : stmt - > encoding ( ) ) ;
2019-05-09 00:18:15 +02:00
pairs - > reserve ( numpairs ) ;
2019-05-01 17:03:33 +02:00
ptr + = sizeof ( unsigned short ) ;
while ( npairs - - ) {
int namelen , idlen ;
unsigned char * nameptr , * idptr ;
sqlsrv_malloc_auto_ptr < name_id_pair > pair ;
pair = new ( sqlsrv_malloc ( sizeof ( name_id_pair ) ) ) name_id_pair ( ) ;
sqlsrv_malloc_auto_ptr < char > name ;
sqlsrv_malloc_auto_ptr < char > id ;
namelen = * ptr + + ;
nameptr = ptr ;
pair - > name_len = namelen ;
convert_sensivity_field ( stmt , encoding , nameptr , namelen , ( char * * ) & name ) ;
pair - > name = name ;
ptr + = namelen * 2 ;
idlen = * ptr + + ;
idptr = ptr ;
ptr + = idlen * 2 ;
pair - > id_len = idlen ;
convert_sensivity_field ( stmt , encoding , idptr , idlen , ( char * * ) & id ) ;
pair - > id = id ;
2019-05-09 00:18:15 +02:00
pairs - > push_back ( pair . get ( ) ) ;
2019-05-01 17:03:33 +02:00
pair . transferred ( ) ;
}
* pptr = ptr ;
}
void parse_column_sensitivity_props ( _Inout_ sensitivity_metadata * meta , _Inout_ unsigned char * * pptr )
{
unsigned char * ptr = * pptr ;
unsigned short ncols ;
// Get number of columns
meta - > num_columns = ncols = * ( reinterpret_cast < unsigned short * > ( ptr ) ) ;
// Move forward
ptr + = sizeof ( unsigned short ) ;
while ( ncols - - ) {
unsigned short npairs = * ( reinterpret_cast < unsigned short * > ( ptr ) ) ;
ptr + = sizeof ( unsigned short ) ;
column_sensitivity column ;
column . num_pairs = npairs ;
while ( npairs - - ) {
label_infotype_pair pair ;
unsigned short labelidx , typeidx ;
labelidx = * ( reinterpret_cast < unsigned short * > ( ptr ) ) ;
ptr + = sizeof ( unsigned short ) ;
typeidx = * ( reinterpret_cast < unsigned short * > ( ptr ) ) ;
ptr + = sizeof ( unsigned short ) ;
pair . label_idx = labelidx ;
pair . infotype_idx = typeidx ;
column . label_info_pairs . push_back ( pair ) ;
}
meta - > columns_sensitivity . push_back ( column ) ;
}
* pptr = ptr ;
}
USHORT fill_column_sensitivity_array ( _Inout_ sqlsrv_stmt * stmt , _In_ SQLSMALLINT colno , _Inout_ zval * return_array TSRMLS_CC )
{
sensitivity_metadata * meta = stmt - > current_sensitivity_metadata ;
if ( meta = = NULL ) {
return 0 ;
}
2019-05-09 00:18:15 +02:00
2019-05-01 17:03:33 +02:00
SQLSRV_ASSERT ( colno > = 0 & & colno < meta - > num_columns , " fill_column_sensitivity_array: column number out of bounds " ) ;
zval data_classification ;
ZVAL_UNDEF ( & data_classification ) ;
core : : sqlsrv_array_init ( * stmt , & data_classification TSRMLS_CC ) ;
USHORT num_pairs = meta - > columns_sensitivity [ colno ] . num_pairs ;
if ( num_pairs = = 0 ) {
core : : sqlsrv_add_assoc_zval ( * stmt , return_array , DATA_CLASS , & data_classification TSRMLS_CC ) ;
return 0 ;
}
zval sensitivity_properties ;
ZVAL_UNDEF ( & sensitivity_properties ) ;
core : : sqlsrv_array_init ( * stmt , & sensitivity_properties TSRMLS_CC ) ;
for ( USHORT j = 0 ; j < num_pairs ; j + + ) {
zval label_array , infotype_array ;
ZVAL_UNDEF ( & label_array ) ;
ZVAL_UNDEF ( & infotype_array ) ;
core : : sqlsrv_array_init ( * stmt , & label_array TSRMLS_CC ) ;
core : : sqlsrv_array_init ( * stmt , & infotype_array TSRMLS_CC ) ;
USHORT labelidx = meta - > columns_sensitivity [ colno ] . label_info_pairs [ j ] . label_idx ;
USHORT typeidx = meta - > columns_sensitivity [ colno ] . label_info_pairs [ j ] . infotype_idx ;
char * label = meta - > labels [ labelidx ] - > name ;
char * label_id = meta - > labels [ labelidx ] - > id ;
char * infotype = meta - > infotypes [ typeidx ] - > name ;
char * infotype_id = meta - > infotypes [ typeidx ] - > id ;
core : : sqlsrv_add_assoc_string ( * stmt , & label_array , NAME , label , 1 TSRMLS_CC ) ;
core : : sqlsrv_add_assoc_string ( * stmt , & label_array , ID , label_id , 1 TSRMLS_CC ) ;
core : : sqlsrv_add_assoc_zval ( * stmt , & sensitivity_properties , LABEL , & label_array TSRMLS_CC ) ;
core : : sqlsrv_add_assoc_string ( * stmt , & infotype_array , NAME , infotype , 1 TSRMLS_CC ) ;
core : : sqlsrv_add_assoc_string ( * stmt , & infotype_array , ID , infotype_id , 1 TSRMLS_CC ) ;
core : : sqlsrv_add_assoc_zval ( * stmt , & sensitivity_properties , INFOTYPE , & infotype_array TSRMLS_CC ) ;
// add the pair of sensitivity properties to data_classification
core : : sqlsrv_add_next_index_zval ( * stmt , & data_classification , & sensitivity_properties TSRMLS_CC ) ;
}
// add data classfication as associative array
core : : sqlsrv_add_assoc_zval ( * stmt , return_array , DATA_CLASS , & data_classification TSRMLS_CC ) ;
return num_pairs ;
}
void sensitivity_metadata : : reset ( )
{
std : : for_each ( labels . begin ( ) , labels . end ( ) , name_id_pair_free ) ;
labels . clear ( ) ;
std : : for_each ( infotypes . begin ( ) , infotypes . end ( ) , name_id_pair_free ) ;
infotypes . clear ( ) ;
columns_sensitivity . clear ( ) ;
}
2019-05-10 01:56:18 +02:00
} // namespace data_classification