diff --git a/source/shared/core_conn.cpp b/source/shared/core_conn.cpp index 3d195fe9c..527143557 100644 --- a/source/shared/core_conn.cpp +++ b/source/shared/core_conn.cpp @@ -386,7 +386,7 @@ SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& con // We only support UTF-8 encoding for connection string. // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW - wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast( conn_str.length() ), &wconn_len ); + wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast( conn_str.length() ), &wconn_len, true ); CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message()) { diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index b7a8c3921..c6a0eb97a 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -1808,7 +1808,7 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set { bool convert_string_from_utf16_inplace( _In_ SQLSRV_ENCODING encoding, _Inout_updates_z_(len) char** string, _Inout_ SQLLEN& len); bool validate_string( _In_ char* string, _In_ SQLLEN& len); 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 ); -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, _Out_ unsigned int* utf16_len ); +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, _Out_ unsigned int* utf16_len, bool use_strict_conversion = false ); //********************************************************************************************************************************* // Error handling routines and Predefined Errors diff --git a/source/shared/core_util.cpp b/source/shared/core_util.cpp index 8fea03358..e5427f97a 100644 --- a/source/shared/core_util.cpp +++ b/source/shared/core_util.cpp @@ -34,7 +34,7 @@ char last_err_msg[2048] = {'\0'}; // 2k to hold the error messages 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, - _In_ unsigned int utf16_len ); + _In_ unsigned int utf16_len, bool use_strict_conversion = false ); } // SQLSTATE for all internal errors @@ -172,11 +172,11 @@ bool convert_string_from_utf16( _In_ SQLSRV_ENCODING encoding, _In_reads_bytes_( // allocation of the destination string. An empty string passed in returns // failure since it's a failure case for convert_string_from_default_encoding. 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, - _Out_ unsigned int* utf16_len ) + _Out_ unsigned int* utf16_len, bool use_strict_conversion ) { *utf16_len = (mbcs_len + 1); SQLWCHAR* utf16_string = reinterpret_cast( sqlsrv_malloc( *utf16_len * sizeof( SQLWCHAR ))); - *utf16_len = convert_string_from_default_encoding( php_encoding, mbcs_string, mbcs_len, utf16_string, *utf16_len ); + *utf16_len = convert_string_from_default_encoding( php_encoding, mbcs_string, mbcs_len, utf16_string, *utf16_len, use_strict_conversion ); if( *utf16_len == 0 ) { // we preserve the error and reset it because sqlsrv_free resets the last error @@ -384,7 +384,7 @@ namespace { // to convert. 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, - _In_ unsigned int utf16_len ) + _In_ unsigned int utf16_len, bool use_strict_conversion ) { unsigned int win_encoding = CP_ACP; switch( php_encoding ) { @@ -399,8 +399,14 @@ unsigned int convert_string_from_default_encoding( _In_ unsigned int php_encodin win_encoding = php_encoding; break; } -#ifndef _WIN32 - unsigned int required_len = SystemLocale::ToUtf16( win_encoding, mbcs_in_string, mbcs_len, utf16_out_string, utf16_len ); +#ifndef _WIN32 + unsigned int required_len; + if (use_strict_conversion) { + 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 ); + } #else unsigned int required_len = MultiByteToWideChar( win_encoding, MB_ERR_INVALID_CHARS, mbcs_in_string, mbcs_len, utf16_out_string, utf16_len ); #endif // !_Win32 @@ -610,4 +616,4 @@ namespace data_classification { columns_sensitivity.clear(); } -} // namespace data_classification \ No newline at end of file +} // namespace data_classification diff --git a/source/sqlsrv/php_sqlsrv_int.h b/source/sqlsrv/php_sqlsrv_int.h index c294f465b..bd555fe10 100644 --- a/source/sqlsrv/php_sqlsrv_int.h +++ b/source/sqlsrv/php_sqlsrv_int.h @@ -214,12 +214,12 @@ bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_err // returned in utf16_out_string. 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) wchar_t* utf16_out_string, - _In_ unsigned int utf16_len ); + _In_ unsigned int utf16_len, bool use_strict_conversion = false ); // create a wide char string from the passed in mbcs string. NULL is returned if the string // could not be created. No error is posted by this function. utf16_len is the number of // wchar_t characters, not the number of bytes. SQLWCHAR* utf16_string_from_mbcs_string( _In_ unsigned int php_encoding, _In_reads_bytes_(mbcs_len) const char* mbcs_string, - _In_ unsigned int mbcs_len, _Out_ unsigned int* utf16_len ); + _In_ unsigned int mbcs_len, _Out_ unsigned int* utf16_len, bool use_strict_conversion = false ); // *** internal error macros and functions *** bool handle_error( sqlsrv_context const* ctx, int log_subsystem, const char* function, diff --git a/test/functional/pdo_sqlsrv/pdo_construct_attr_errors.phpt b/test/functional/pdo_sqlsrv/pdo_construct_attr_errors.phpt index a31974d94..7b0f101a6 100644 --- a/test/functional/pdo_sqlsrv/pdo_construct_attr_errors.phpt +++ b/test/functional/pdo_sqlsrv/pdo_construct_attr_errors.phpt @@ -38,7 +38,7 @@ function invalidServer() echo "Should have failed to connect to invalid server.\n"; } catch (PDOException $e) { $error1 = '*Login timeout expired'; - $error2 = '*An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page*'; + $error2 = '*An error occurred translating the connection string to UTF-16: *'; if (fnmatch($error1, $e->getMessage()) || fnmatch($error2, $e->getMessage())) { ; // matched at least one of the expected error messages } else { @@ -102,7 +102,7 @@ function invalidPassword() $options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); // Possible errors - $error = "*An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page.*"; + $error = "*An error occurred translating the connection string to UTF-16: *"; $error1 = "*Login failed for user \'*\'."; $error2 = "*Login timeout expired*"; diff --git a/test/functional/sqlsrv/sqlsrv_connStr.phpt b/test/functional/sqlsrv/sqlsrv_connStr.phpt index 144ceb94a..7c92bd997 100644 --- a/test/functional/sqlsrv/sqlsrv_connStr.phpt +++ b/test/functional/sqlsrv/sqlsrv_connStr.phpt @@ -1,11 +1,18 @@ --TEST-- UTF-8 connection strings --SKIPIF-- - + --FILE-- 'gibberish' )); if ($c !== false) { fatalError("Should have errored on an invalid encoding."); } -print_r(sqlsrv_errors()); +checkErrors($gibberishEncoding); $c = connect(array( 'CharacterSet' => SQLSRV_ENC_BINARY )); if ($c !== false) { fatalError("Should have errored on an invalid encoding."); } -print_r(sqlsrv_errors()); +checkErrors($binaryEncoding); $c = connect(array( 'CharacterSet' => SQLSRV_ENC_CHAR )); if ($c === false) { @@ -50,7 +68,7 @@ $c = sqlsrv_connect($server_invalid, array( 'Database' => 'test', 'CharacterSet' if ($c !== false) { fatalError("sqlsrv_connect(1) should have failed"); } -print_r(sqlsrv_errors()); +checkErrors($utf16Error); // APP has a UTF-8 name $c = connect(array( @@ -67,7 +85,7 @@ $c = connect(array( if ($c !== false) { fatalError("sqlsrv_connect(3) should have failed"); } -print_r(sqlsrv_errors()); +checkErrors($userLoginFailed); // invalid UTF-8 in the pwd $c = connect(array( @@ -77,89 +95,10 @@ $c = connect(array( if ($c !== false) { fatalError("sqlsrv_connect(4) should have failed"); } -print_r(sqlsrv_errors()); +checkErrors($utf16Error); echo "Test succeeded.\n"; ?> ---EXPECTF-- -Array -( - [0] => Array - ( - [0] => IMSSP - [SQLSTATE] => IMSSP - [1] => -48 - [code] => -48 - [2] => The encoding 'gibberish' is not a supported encoding for the CharacterSet connection option. - [message] => The encoding 'gibberish' is not a supported encoding for the CharacterSet connection option. - ) - -) -Array -( - [0] => Array - ( - [0] => IMSSP - [SQLSTATE] => IMSSP - [1] => -48 - [code] => -48 - [2] => The encoding 'binary' is not a supported encoding for the CharacterSet connection option. - [message] => The encoding 'binary' is not a supported encoding for the CharacterSet connection option. - ) - -) -Array -( - [0] => Array - ( - [0] => IMSSP - [SQLSTATE] => IMSSP - [1] => -47 - [code] => -47 - [2] => An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page. - - [message] => An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page. - - ) - -) -Array -( - [0] => Array - ( - [0] => 28000 - [SQLSTATE] => 28000 - [1] => 18456 - [code] => 18456 - [2] => %SLogin failed for user '%s'. - [message] => %SLogin failed for user '%s'. - ) - - [1] => Array - ( - [0] => 28000 - [SQLSTATE] => 28000 - [1] => 18456 - [code] => 18456 - [2] => %SLogin failed for user '%s'. - [message] => %SLogin failed for user '%s'. - ) - -) -Array -( - [0] => Array - ( - [0] => IMSSP - [SQLSTATE] => IMSSP - [1] => -47 - [code] => -47 - [2] => An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page. - - [message] => An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page. - - ) - -) +--EXPECT-- Test succeeded.