Skip to content

Commit

Permalink
Merge pull request #989 from david-puglielli/issue-432-fix
Browse files Browse the repository at this point in the history
Issue #432 fix
  • Loading branch information
david-puglielli authored May 13, 2019
2 parents e152459 + 60f20bd commit f369ce6
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 99 deletions.
2 changes: 1 addition & 1 deletion source/shared/core_conn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned int>( conn_str.length() ), &wconn_len );
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>( conn_str.length() ), &wconn_len, true );

CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message())
{
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 @@ -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
Expand Down
20 changes: 13 additions & 7 deletions source/shared/core_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<SQLWCHAR*>( 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
Expand Down Expand Up @@ -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 ) {
Expand All @@ -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
Expand Down Expand Up @@ -610,4 +616,4 @@ namespace data_classification {

columns_sensitivity.clear();
}
} // namespace data_classification
} // namespace data_classification
4 changes: 2 additions & 2 deletions source/sqlsrv/php_sqlsrv_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions test/functional/pdo_sqlsrv/pdo_construct_attr_errors.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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*";

Expand Down
111 changes: 25 additions & 86 deletions test/functional/sqlsrv/sqlsrv_connStr.phpt
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
--TEST--
UTF-8 connection strings
--SKIPIF--
<?php require('skipif_unix.inc'); ?>
<?php require('skipif.inc'); ?>
--FILE--
<?php

require_once('MsSetup.inc');

// Expected errors
$gibberishEncoding = array('IMSSP', '-48', "The encoding 'gibberish' is not a supported encoding for the CharacterSet connection option.");
$binaryEncoding = array('IMSSP', '-48', "The encoding 'binary' is not a supported encoding for the CharacterSet connection option.");
$utf16Error = array('IMSSP', '-47', "An error occurred translating the connection string to UTF-16: *");
$userLoginFailed = array('28000', '18456', "*Login failed for user *");

function connect($options=array())
{
global $server, $uid, $pwd, $databaseName;
Expand All @@ -22,6 +29,17 @@ function connect($options=array())
return sqlsrv_connect($server, $options);
}

function checkErrors($expectedError)
{
// On Windows one call returns two identical errors, so just take the first element
$error = sqlsrv_errors()[0];
if (!fnmatch($expectedError[0], $error[0]) ||
!fnmatch($expectedError[1], $error[1]) ||
!fnmatch($expectedError[2], $error[2])) {
fatalError("Errors do not match!\n");
}
}

sqlsrv_configure('WarningsReturnAsErrors', 0);
sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL);

Expand All @@ -30,13 +48,13 @@ $c = connect(array( 'CharacterSet' => '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) {
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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.

0 comments on commit f369ce6

Please sign in to comment.