SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string()

in source/shared/core_results.cpp [1321:1412]


SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( _In_ SQLSMALLINT field_index, _Inout_updates_bytes_to_(buffer_length, *out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
                                                             _Inout_ SQLLEN* out_buffer_length )
{
    SQLSRV_ASSERT( last_error == 0, "Pending error for sqlsrv_buffered_results_set::wide_to_system_string" );

    SQLRETURN r = SQL_ERROR;
    unsigned char* row = get_row();

    SQLCHAR* field_data = NULL;
    SQLLEN field_len = 0;

    // if this is the first time called for this field, just convert the entire string to system first then
    // use that to read from instead of converting chunk by chunk.  This is because it's impossible to know
    // the total length of the string for output_buffer_length without doing the conversion and returning
    // SQL_NO_TOTAL is not consistent with what our other conversion functions do (system_to_wide_string and
    // to_same_string).

    if( read_so_far == 0 ) {

        if( meta[field_index].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) {

            field_len = **reinterpret_cast<SQLLEN**>( &row[meta[field_index].offset] );
            field_data = *reinterpret_cast<SQLCHAR**>( &row[meta[field_index].offset] ) + sizeof( SQLULEN ) + read_so_far;
        }
        else {

            field_len = *reinterpret_cast<SQLLEN*>( &row[meta[field_index].offset] );
            field_data = &row[meta[field_index].offset] + sizeof( SQLULEN ) + read_so_far;
        }

        if ( field_len == 0 ) { // empty string, no need for conversion
            *out_buffer_length = 0;
            return SQL_SUCCESS;
        }

        // allocate enough to handle WC -> DBCS conversion if it happens
        temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof(char), sizeof(char)));
			
#ifndef _WIN32		
        temp_length = SystemLocale::FromUtf16( CP_ACP, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)),
                                 (LPSTR) temp_string.get(), static_cast<int>(field_len) );
#else								 			
        BOOL default_char_used = FALSE;
        char default_char = '?';

        temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)),
                                           (LPSTR) temp_string.get(), static_cast<int>(field_len), &default_char, &default_char_used );
#endif	// !_WIN32	
        if( temp_length == 0 ) {

            switch( GetLastError() ) {

                case ERROR_NO_UNICODE_TRANSLATION:
                    last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error )))
                        sqlsrv_error( (SQLCHAR*) "IMSSP", (SQLCHAR*) "Invalid Unicode translation", -1 );
                    break;
                default:
                    SQLSRV_ASSERT( false, "Severe error translating Unicode" );
                    break;
            }

            return SQL_ERROR;
        }
    }

    *out_buffer_length = (temp_length - read_so_far);

    SQLLEN to_copy = 0;

    if( (size_t) buffer_length < (temp_length - read_so_far + sizeof(char))) {

        to_copy = buffer_length - sizeof(char);
        last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) 
            sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 );
        r = SQL_SUCCESS_WITH_INFO;
    }
    else {

        to_copy = (temp_length - read_so_far);
        r = SQL_SUCCESS;
    }

    if( to_copy > 0 ) {
        memcpy_s( buffer, buffer_length, temp_string.get() + read_so_far, to_copy );
    }
    SQLSRV_ASSERT( to_copy >= 0, "Invalid field copy length" );
    OACR_WARNING_SUPPRESS( BUFFER_UNDERFLOW, "Buffer length verified above" );
    ((SQLCHAR*) buffer)[to_copy] = '\0';
    read_so_far += to_copy;

    return r;
}