Simplified get_field_as_string and made it more robust (#1265)
This commit is contained in:
parent
a14cb70ad3
commit
af61d06bfb
|
@ -1621,8 +1621,8 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
|
||||||
*field_len = len;
|
*field_len = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype sqlsrv_php_type,
|
void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype sqlsrv_php_type,
|
||||||
_Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len )
|
_Inout_updates_bytes_(*field_len) void *&field_value, _Inout_ SQLLEN *field_len)
|
||||||
{
|
{
|
||||||
SQLRETURN r;
|
SQLRETURN r;
|
||||||
SQLSMALLINT c_type;
|
SQLSMALLINT c_type;
|
||||||
|
@ -1631,7 +1631,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
SQLLEN field_len_temp = 0;
|
SQLLEN field_len_temp = 0;
|
||||||
SQLLEN sql_display_size = 0;
|
SQLLEN sql_display_size = 0;
|
||||||
char* field_value_temp = NULL;
|
char* field_value_temp = NULL;
|
||||||
unsigned int intial_field_len = INITIAL_FIELD_STRING_LEN;
|
unsigned int initial_field_len = INITIAL_FIELD_STRING_LEN;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
@ -1670,222 +1670,144 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
if (sqlsrv_php_type.typeinfo.encoding == CP_UTF8 && !is_a_numeric_type(sql_field_type)) {
|
if (sqlsrv_php_type.typeinfo.encoding == CP_UTF8 && !is_a_numeric_type(sql_field_type)) {
|
||||||
c_type = SQL_C_WCHAR;
|
c_type = SQL_C_WCHAR;
|
||||||
extra = sizeof(SQLWCHAR);
|
extra = sizeof(SQLWCHAR);
|
||||||
|
|
||||||
|
sql_display_size = (sql_display_size * sizeof(SQLWCHAR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a large type, then read the first few bytes to get the actual length from SQLGetData
|
// If this is a large type, then read the first chunk to get the actual length from SQLGetData
|
||||||
// The user may use "SET TEXTSIZE" to specify the size of varchar(max), nvarchar(max),
|
// The user may use "SET TEXTSIZE" to specify the size of varchar(max), nvarchar(max),
|
||||||
// varbinary(max), text, ntext, and image data returned by a SELECT statement.
|
// varbinary(max), text, ntext, and image data returned by a SELECT statement.
|
||||||
// For varchar(max) and nvarchar(max), sql_display_size will be 0, regardless
|
// For varbinary(max), varchar(max) and nvarchar(max), sql_display_size will be 0, regardless
|
||||||
if (sql_display_size == 0 || sql_display_size == INT_MAX ||
|
if (sql_display_size == 0 ||
|
||||||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ||
|
(sql_field_type == SQL_WLONGVARCHAR || sql_field_type == SQL_LONGVARCHAR || sql_field_type == SQL_LONGVARBINARY)) {
|
||||||
(sql_display_size > SQL_SERVER_MAX_FIELD_SIZE &&
|
|
||||||
(sql_field_type == SQL_WLONGVARCHAR || sql_field_type == SQL_LONGVARCHAR || sql_field_type == SQL_LONGVARBINARY))) {
|
|
||||||
|
|
||||||
field_len_temp = intial_field_len;
|
field_len_temp = initial_field_len;
|
||||||
|
field_value_temp = static_cast<char*>(sqlsrv_malloc(field_len_temp + extra + 1));
|
||||||
SQLLEN initiallen = field_len_temp + extra;
|
r = stmt->current_results->get_data(field_index + 1, c_type, field_value_temp, (field_len_temp + extra), &field_len_temp, false /*handle_warning*/);
|
||||||
|
} else {
|
||||||
field_value_temp = static_cast<char*>( sqlsrv_malloc( field_len_temp + extra + 1 ));
|
field_len_temp = sql_display_size;
|
||||||
|
field_value_temp = static_cast<char*>(sqlsrv_malloc(sql_display_size + extra + 1));
|
||||||
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ),
|
|
||||||
&field_len_temp, false /*handle_warning*/ );
|
|
||||||
|
|
||||||
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if( field_len_temp == SQL_NULL_DATA ) {
|
|
||||||
field_value = NULL;
|
|
||||||
sqlsrv_free( field_value_temp );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( r == SQL_SUCCESS_WITH_INFO ) {
|
|
||||||
|
|
||||||
SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = {L'\0'};
|
|
||||||
SQLSMALLINT len = 0;
|
|
||||||
|
|
||||||
stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
|
|
||||||
|
|
||||||
// with Linux connection pooling may not get a truncated warning back but the actual field_len_temp
|
|
||||||
// can be greater than the initallen value.
|
|
||||||
#ifndef _WIN32
|
|
||||||
if( is_truncated_warning( state ) || initiallen < field_len_temp) {
|
|
||||||
#else
|
|
||||||
if( is_truncated_warning( state ) ) {
|
|
||||||
#endif // !_WIN32
|
|
||||||
|
|
||||||
SQLLEN dummy_field_len = 0;
|
|
||||||
|
|
||||||
// for XML (and possibly other conditions) the field length returned is not the real field length, so
|
|
||||||
// in every pass, we double the allocation size to retrieve all the contents.
|
|
||||||
if( field_len_temp == SQL_NO_TOTAL ) {
|
|
||||||
|
|
||||||
// reset the field_len_temp
|
|
||||||
field_len_temp = intial_field_len;
|
|
||||||
|
|
||||||
do {
|
|
||||||
SQLLEN initial_field_len = field_len_temp;
|
|
||||||
// Double the size.
|
|
||||||
field_len_temp *= 2;
|
|
||||||
|
|
||||||
field_value_temp = static_cast<char*>( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 ));
|
|
||||||
|
|
||||||
field_len_temp -= initial_field_len;
|
|
||||||
|
|
||||||
// Get the rest of the data.
|
|
||||||
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len,
|
|
||||||
field_len_temp + extra, &dummy_field_len, false /*handle_warning*/ );
|
|
||||||
// the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL
|
|
||||||
// so we calculate the actual length of the string with that.
|
|
||||||
if ( dummy_field_len != SQL_NO_TOTAL )
|
|
||||||
field_len_temp += dummy_field_len;
|
|
||||||
else
|
|
||||||
field_len_temp += initial_field_len;
|
|
||||||
|
|
||||||
if( r == SQL_SUCCESS_WITH_INFO ) {
|
|
||||||
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state ));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// the real field length is returned here, thus no need to double the allocation size here, just have to
|
|
||||||
// allocate field_len_temp (which is the field length retrieved from the first SQLGetData
|
|
||||||
field_value_temp = static_cast<char*>( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 ));
|
|
||||||
|
|
||||||
// We have already received intial_field_len size data.
|
|
||||||
field_len_temp -= intial_field_len;
|
|
||||||
|
|
||||||
// Get the rest of the data.
|
|
||||||
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + intial_field_len,
|
|
||||||
field_len_temp + extra, &dummy_field_len, true /*handle_warning*/ );
|
|
||||||
field_len_temp += intial_field_len;
|
|
||||||
|
|
||||||
if( dummy_field_len == SQL_NULL_DATA ) {
|
|
||||||
field_value = NULL;
|
|
||||||
sqlsrv_free( field_value_temp );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // if( is_truncation_warning ( state ) )
|
|
||||||
else {
|
|
||||||
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // if( r == SQL_SUCCESS_WITH_INFO )
|
|
||||||
|
|
||||||
if (c_type == SQL_C_WCHAR) {
|
|
||||||
bool converted = convert_string_from_utf16_inplace( static_cast<SQLSRV_ENCODING>( sqlsrv_php_type.typeinfo.encoding ),
|
|
||||||
&field_value_temp, field_len_temp );
|
|
||||||
|
|
||||||
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. )
|
|
||||||
|
|
||||||
else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) {
|
|
||||||
|
|
||||||
// only allow binary retrievals for char and binary types. All others get a string converted
|
|
||||||
// to the encoding type they asked for.
|
|
||||||
|
|
||||||
// null terminator
|
|
||||||
if( c_type == SQL_C_CHAR ) {
|
|
||||||
sql_display_size += sizeof( SQLCHAR );
|
|
||||||
}
|
|
||||||
|
|
||||||
// For WCHAR multiply by sizeof(WCHAR) and include the null terminator
|
|
||||||
else if( c_type == SQL_C_WCHAR ) {
|
|
||||||
sql_display_size = (sql_display_size * sizeof(WCHAR)) + sizeof(WCHAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
field_value_temp = static_cast<char*>( sqlsrv_malloc( sql_display_size + extra + 1 ));
|
|
||||||
|
|
||||||
// get the data
|
// get the data
|
||||||
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size,
|
r = stmt->current_results->get_data(field_index + 1, c_type, field_value_temp, sql_display_size + extra, &field_len_temp, false /*handle_warning*/);
|
||||||
&field_len_temp, true /*handle_warning*/ );
|
|
||||||
CHECK_SQL_ERROR( r, stmt ) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if( field_len_temp == SQL_NULL_DATA ) {
|
|
||||||
field_value = NULL;
|
|
||||||
sqlsrv_free( field_value_temp );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c_type == SQL_C_WCHAR) {
|
|
||||||
bool converted = convert_string_from_utf16_inplace( static_cast<SQLSRV_ENCODING>( sqlsrv_php_type.typeinfo.encoding ),
|
|
||||||
&field_value_temp, field_len_temp );
|
|
||||||
|
|
||||||
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) {
|
|
||||||
throw core::CoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stmt->format_decimals && (sql_field_type == SQL_DECIMAL || sql_field_type == SQL_NUMERIC)) {
|
|
||||||
// number of decimal places only affect money / smallmoney fields
|
|
||||||
SQLSMALLINT decimal_places = (stmt->current_meta_data[field_index]->field_is_money_type) ? stmt->decimal_places : NO_CHANGE_DECIMAL_PLACES;
|
|
||||||
format_decimal_numbers(decimal_places, stmt->current_meta_data[field_index]->field_scale, field_value_temp, &field_len_temp);
|
|
||||||
}
|
|
||||||
} // else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE )
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
DIE( "Invalid sql_display_size" );
|
|
||||||
return; // to eliminate a warning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
field_value = field_value_temp;
|
CHECK_CUSTOM_ERROR((r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index) {
|
||||||
*field_len = field_len_temp;
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_len_temp == SQL_NULL_DATA) {
|
||||||
|
field_value = NULL;
|
||||||
|
sqlsrv_free(field_value_temp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == SQL_SUCCESS_WITH_INFO) {
|
||||||
|
SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = { L'\0' };
|
||||||
|
SQLSMALLINT len = 0;
|
||||||
|
|
||||||
|
stmt->current_results->get_diag_field(1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len);
|
||||||
|
if (is_truncated_warning(state)) {
|
||||||
|
SQLLEN chunk_field_len = 0;
|
||||||
|
|
||||||
|
// for XML (and possibly other conditions) the field length returned is not the real field length, so
|
||||||
|
// in every pass, we double the allocation size to retrieve all the contents.
|
||||||
|
if (field_len_temp == SQL_NO_TOTAL) {
|
||||||
|
|
||||||
|
// reset the field_len_temp
|
||||||
|
field_len_temp = initial_field_len;
|
||||||
|
|
||||||
|
do {
|
||||||
|
SQLLEN buffer_len = field_len_temp;
|
||||||
|
// Double the size.
|
||||||
|
field_len_temp *= 2;
|
||||||
|
|
||||||
|
field_value_temp = static_cast<char*>(sqlsrv_realloc(field_value_temp, field_len_temp + extra + 1));
|
||||||
|
|
||||||
|
field_len_temp -= buffer_len;
|
||||||
|
|
||||||
|
// Get the rest of the data
|
||||||
|
r = stmt->current_results->get_data(field_index + 1, c_type, field_value_temp + buffer_len,
|
||||||
|
field_len_temp + extra, &chunk_field_len, false /*handle_warning*/);
|
||||||
|
// the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL
|
||||||
|
// so we calculate the actual length of the string with that.
|
||||||
|
if (chunk_field_len != SQL_NO_TOTAL)
|
||||||
|
field_len_temp += chunk_field_len;
|
||||||
|
else
|
||||||
|
field_len_temp += buffer_len;
|
||||||
|
|
||||||
|
if (r == SQL_SUCCESS_WITH_INFO) {
|
||||||
|
core::SQLGetDiagField(stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len);
|
||||||
|
}
|
||||||
|
} while (r == SQL_SUCCESS_WITH_INFO && is_truncated_warning(state));
|
||||||
|
} // if (field_len_temp == SQL_NO_TOTAL)
|
||||||
|
else {
|
||||||
|
// The field length (or its estimate) is returned, thus no need to double the allocation size.
|
||||||
|
// Allocate field_len_temp (which is the field length retrieved from the first SQLGetData) but with some padding
|
||||||
|
// because there is a chance that the estimated field_len_temp is not accurate enough
|
||||||
|
SQLLEN buffer_len = 50;
|
||||||
|
field_value_temp = static_cast<char*>(sqlsrv_realloc(field_value_temp, field_len_temp + buffer_len + 1));
|
||||||
|
field_len_temp -= initial_field_len;
|
||||||
|
|
||||||
|
// Get the rest of the data
|
||||||
|
r = stmt->current_results->get_data(field_index + 1, c_type, field_value_temp + initial_field_len,
|
||||||
|
field_len_temp + buffer_len, &chunk_field_len, false /*handle_warning*/);
|
||||||
|
field_len_temp = initial_field_len + chunk_field_len;
|
||||||
|
|
||||||
|
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reallocate field_value_temp next
|
||||||
|
field_value_temp = static_cast<char*>(sqlsrv_realloc(field_value_temp, field_len_temp + extra + 1));
|
||||||
|
}
|
||||||
|
} // if (is_truncated_warning(state))
|
||||||
|
} // if (r == SQL_SUCCESS_WITH_INFO)
|
||||||
|
|
||||||
|
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c_type == SQL_C_WCHAR) {
|
||||||
|
bool converted = convert_string_from_utf16_inplace(static_cast<SQLSRV_ENCODING>(sqlsrv_php_type.typeinfo.encoding),
|
||||||
|
&field_value_temp, field_len_temp);
|
||||||
|
|
||||||
|
CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt->format_decimals && (sql_field_type == SQL_DECIMAL || sql_field_type == SQL_NUMERIC)) {
|
||||||
|
// number of decimal places only affect money / smallmoney fields
|
||||||
|
SQLSMALLINT decimal_places = (stmt->current_meta_data[field_index]->field_is_money_type) ? stmt->decimal_places : NO_CHANGE_DECIMAL_PLACES;
|
||||||
|
format_decimal_numbers(decimal_places, stmt->current_meta_data[field_index]->field_scale, field_value_temp, &field_len_temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalized the returned values and set field_len to 0 if field_len_temp is negative (which may happen with unixODBC connection pooling)
|
||||||
|
field_value = field_value_temp;
|
||||||
|
*field_len = (field_len_temp > 0) ? field_len_temp : 0;
|
||||||
|
|
||||||
// prevent a warning in debug mode about strings not being NULL terminated. Even though nulls are not necessary, the PHP
|
// prevent a warning in debug mode about strings not being NULL terminated. Even though nulls are not necessary, the PHP
|
||||||
// runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode.
|
// runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode.
|
||||||
// SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary
|
// SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and add 1 to fill the null terminator
|
||||||
// operator to set add 1 to fill the null terminator
|
if (field_len_temp > 0) {
|
||||||
|
|
||||||
// with unixODBC connection pooling sometimes field_len_temp can be SQL_NO_DATA.
|
|
||||||
// In that cause do not set null terminator and set length to 0.
|
|
||||||
if ( field_len_temp > 0 )
|
|
||||||
{
|
|
||||||
field_value_temp[field_len_temp] = '\0';
|
field_value_temp[field_len_temp] = '\0';
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
*field_len = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (core::CoreException&) {
|
||||||
catch( core::CoreException& ) {
|
|
||||||
|
|
||||||
field_value = NULL;
|
field_value = NULL;
|
||||||
*field_len = 0;
|
*field_len = 0;
|
||||||
sqlsrv_free( field_value_temp );
|
sqlsrv_free(field_value_temp);
|
||||||
throw;
|
throw;
|
||||||
}
|
} catch (...) {
|
||||||
catch ( ... ) {
|
|
||||||
|
|
||||||
field_value = NULL;
|
field_value = NULL;
|
||||||
*field_len = 0;
|
*field_len = 0;
|
||||||
sqlsrv_free( field_value_temp );
|
sqlsrv_free(field_value_temp);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// return the option from the stmt_opts array that matches the key. If no option found,
|
// return the option from the stmt_opts array that matches the key. If no option found,
|
||||||
// NULL is returned.
|
// NULL is returned.
|
||||||
|
|
||||||
|
|
58
test/functional/pdo_sqlsrv/pdo_1261_test_ascii_utf8.phpt
Normal file
58
test/functional/pdo_sqlsrv/pdo_1261_test_ascii_utf8.phpt
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
--TEST--
|
||||||
|
Verify Github Issue 1261 is fixed.
|
||||||
|
--DESCRIPTION--
|
||||||
|
This test should already pass in Windows so it is mainly aimed for non-Windows settings where UTF-8 is the default encoding. ODBC warnings are handled differently with pdo_sqlsrv so logging is used to checking the warnings.
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
require_once("MsSetup.inc");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$logFilename = 'php_errors.log';
|
||||||
|
$logFilepath = dirname(__FILE__).'/'.$logFilename;
|
||||||
|
ini_set('log_errors', '1');
|
||||||
|
ini_set('pdo_sqlsrv.log_severity', '2');
|
||||||
|
ini_set('error_log', $logFilepath);
|
||||||
|
|
||||||
|
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName;", $uid, $pwd);
|
||||||
|
$conn->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM);
|
||||||
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
# leading string >= 2045 leading to result length > 2048
|
||||||
|
# column must be VARCHAR(MAX) or VARCHAR(2048) (starts working with bigger VARCHAR(n), e.g. 2060)
|
||||||
|
# SQLSRV_ATTR_ENCODING must be set to SQLSRV_ENCODING_SYSTEM (works with PDO::SQLSRV_ENCODING_UTF8)
|
||||||
|
# COLLATE must not be %UTF8% (e.g. Latin1_General_100_CI_AS_SC_UTF8 works)
|
||||||
|
|
||||||
|
$sql = "DROP TABLE IF EXISTS #tmpTest;
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
DECLARE @val VARCHAR(8000) = REPLICATE('a', 2045) + 'ñ';
|
||||||
|
CREATE TABLE #tmpTest (testCol VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS);
|
||||||
|
INSERT INTO #tmpTest (testCol) VALUES (@val);
|
||||||
|
SELECT * from #tmpTest;";
|
||||||
|
|
||||||
|
$stmt = $conn->query($sql);
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
var_dump($row);
|
||||||
|
|
||||||
|
echo file_get_contents($logFilepath);
|
||||||
|
unlink($logFilepath);
|
||||||
|
|
||||||
|
unset($stmt);
|
||||||
|
unset($conn);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
var_dump($e);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
array(1) {
|
||||||
|
["testCol"]=>
|
||||||
|
string(2049) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaañ"
|
||||||
|
}
|
||||||
|
[%s] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
|
||||||
|
[%s] pdo_sqlsrv_db_handle_factory: error code = 5701
|
||||||
|
[%s] pdo_sqlsrv_db_handle_factory: message = [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Changed database context to '%s'.
|
||||||
|
[%s] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
|
||||||
|
[%s] pdo_sqlsrv_db_handle_factory: error code = 5703
|
||||||
|
[%s] pdo_sqlsrv_db_handle_factory: message = [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Changed language setting to %s.
|
||||||
|
|
40
test/functional/sqlsrv/srv_1261_test_ascii_utf8.phpt
Normal file
40
test/functional/sqlsrv/srv_1261_test_ascii_utf8.phpt
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
--TEST--
|
||||||
|
Verify Github Issue 1261 is fixed.
|
||||||
|
--DESCRIPTION--
|
||||||
|
This test should already pass in Windows so it is mainly aimed for non-Windows settings where UTF-8 is the default encoding.
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
sqlsrv_configure('WarningsReturnAsErrors', 1);
|
||||||
|
sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL);
|
||||||
|
require_once('MsCommon.inc');
|
||||||
|
|
||||||
|
$conn = connect(array("CharacterSet" => SQLSRV_ENC_CHAR));
|
||||||
|
|
||||||
|
# leading string >= 2045 leading to result length > 2048
|
||||||
|
# column must be VARCHAR(MAX) or VARCHAR(2048) (starts working with bigger VARCHAR(n), e.g. 2060)
|
||||||
|
# 'CharacterSet' connInfo must be set to SQLSRV_ENC_CHAR (works with UTF-8)
|
||||||
|
# COLLATE must not be %UTF8% (e.g. Latin1_General_100_CI_AS_SC_UTF8 works)
|
||||||
|
|
||||||
|
$sql = "DROP TABLE IF EXISTS #tmpTest;
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
DECLARE @val VARCHAR(8000) = REPLICATE('a', 2045) + 'ñ';
|
||||||
|
CREATE TABLE #tmpTest (testCol VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS);
|
||||||
|
INSERT INTO #tmpTest (testCol) VALUES (@val);
|
||||||
|
SELECT * from #tmpTest;";
|
||||||
|
|
||||||
|
$stmt = sqlsrv_query($conn, $sql);
|
||||||
|
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||||
|
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
||||||
|
var_dump($row, $errors);
|
||||||
|
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
sqlsrv_close($conn);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
array(1) {
|
||||||
|
["testCol"]=>
|
||||||
|
string(2049) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaañ"
|
||||||
|
}
|
||||||
|
NULL
|
Loading…
Reference in a new issue