2016-01-30 03:00:20 +01:00
//---------------------------------------------------------------------------------------------------------------------------------
// File: core_results.cpp
//
// Contents: Result sets
//
2020-09-15 23:25:04 +02:00
// Microsoft Drivers 5.9 for PHP for SQL Server
2016-01-30 03:00:20 +01: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"
# include <functional>
2017-01-25 02:19:18 +01:00
2016-01-30 03:00:20 +01:00
# include <sstream>
2017-01-25 02:19:18 +01:00
# ifndef _WIN32
# include <type_traits>
2017-01-30 22:05:38 +01:00
# endif // !_WIN32
2017-01-25 02:19:18 +01:00
2016-01-30 03:00:20 +01:00
using namespace core ;
namespace {
// *** internal types ***
2017-01-25 02:19:18 +01:00
# if defined(_MSC_VER)
2016-01-30 03:00:20 +01:00
# pragma warning(disable:4200)
2017-01-25 02:19:18 +01:00
# endif
2016-01-30 03:00:20 +01:00
// *** internal constants ***
2017-11-15 20:00:56 +01:00
const int INITIAL_LOB_FIELD_LEN = 2048 ; // base allocation size when retrieving a LOB field
2016-01-30 03:00:20 +01:00
// *** internal functions ***
// return an integral type rounded up to a certain number
template < int align , typename T >
2017-06-22 23:04:34 +02:00
T align_to ( _In_ T number )
2016-01-30 03:00:20 +01:00
{
DEBUG_SQLSRV_ASSERT ( ( number + align ) > number , " Number to align overflowed " ) ;
return ( ( number % align ) = = 0 ) ? number : ( number + align - ( number % align ) ) ;
}
// return a pointer address aligned to a certain address boundary
template < int align , typename T >
2017-06-22 23:04:34 +02:00
T * align_to ( _In_ T * ptr )
2016-01-30 03:00:20 +01:00
{
size_t p_value = ( size_t ) ptr ;
return align_to < align , size_t > ( p_value ) ;
}
// set the nth bit of the bitstream starting at ptr
2017-06-22 23:04:34 +02:00
void set_bit ( _In_ void * ptr , _In_ unsigned int bit )
2016-01-30 03:00:20 +01:00
{
unsigned char * null_bits = reinterpret_cast < unsigned char * > ( ptr ) ;
null_bits + = bit > > 3 ;
* null_bits | = 1 < < ( 7 - ( bit & 0x7 ) ) ;
}
// retrieve the nth bit from the bitstream starting at ptr
2017-06-22 23:04:34 +02:00
bool get_bit ( _In_ void * ptr , _In_ unsigned int bit )
2016-01-30 03:00:20 +01:00
{
unsigned char * null_bits = reinterpret_cast < unsigned char * > ( ptr ) ;
null_bits + = bit > > 3 ;
return ( ( * null_bits & ( 1 < < ( 7 - ( bit & 0x07 ) ) ) ) ! = 0 ) ;
}
// read in LOB field during buffered result creation
2017-06-22 23:04:34 +02:00
SQLPOINTER read_lob_field ( _Inout_ sqlsrv_stmt * stmt , _In_ SQLUSMALLINT field_index , _In_ sqlsrv_buffered_result_set : : meta_data & meta ,
2020-04-21 00:17:21 +02:00
_In_ zend_long mem_used ) ;
2016-01-30 03:00:20 +01:00
// dtor for each row in the cache
2017-06-22 23:04:34 +02:00
void cache_row_dtor ( _In_ zval * data ) ;
2016-01-30 03:00:20 +01:00
2017-06-22 23:04:34 +02:00
size_t get_float_precision ( _In_ SQLLEN buffer_length , _In_ size_t unitsize )
2017-01-25 02:19:18 +01:00
{
SQLSRV_ASSERT ( unitsize ! = 0 , " Invalid unit size! " ) ;
// get to display size by removing the null terminator from buffer length
size_t display_size = ( buffer_length - unitsize ) / unitsize ;
// use the display size to determine the sql type. And if it is a double, set the precision accordingly
// the display sizes are set by the ODBC driver based on the precision of the sql type
// otherwise we can just use the default precision
size_t real_display_size = 14 ;
size_t float_display_size = 24 ;
size_t real_precision = 7 ;
size_t float_precision = 15 ;
// For more information about display sizes for REAL vs FLOAT/DOUBLE: https://msdn.microsoft.com/en-us/library/ms713974(v=vs.85).aspx
// For more information about precision: https://msdn.microsoft.com/en-us/library/ms173773.aspx
// this is the case of sql type float(24) or real
if ( display_size = = real_display_size ) {
return real_precision ;
}
// this is the case of sql type float(53)
else if ( display_size = = float_display_size ) {
return float_precision ;
}
return 0 ;
}
# ifndef _WIN32
// copy the number into a char string using the num_put facet
template < typename Number >
2017-06-23 01:16:30 +02:00
SQLRETURN get_string_from_stream ( _In_ Number number_data , _Out_ std : : basic_string < char > & str_num , _In_ size_t precision , _Out_ sqlsrv_error_auto_ptr & last_error )
2017-01-25 02:19:18 +01:00
{
//std::locale loc( std::locale(""), new std::num_put<char> ); // By default, SQL Server doesn't take user's locale into consideration
std : : locale loc ;
std : : basic_ostringstream < char > os ;
os . precision ( precision ) ;
os . imbue ( loc ) ;
auto itert = std : : ostreambuf_iterator < char > ( os . rdbuf ( ) ) ;
std : : use_facet < std : : num_put < char > > ( loc ) . put ( itert , os , ' ' , number_data ) ;
str_num = os . str ( ) ;
if ( os . fail ( ) ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " IMSSP " , ( SQLCHAR * ) " Failed to convert number to string " , - 1 ) ;
return SQL_ERROR ;
}
return SQL_SUCCESS ;
}
// copy the Char string into the output buffer - check first that it will fit
template < typename Char >
2017-06-22 23:04:34 +02:00
SQLRETURN copy_buffer ( _Out_writes_bytes_to_opt_ ( out_buffer_lenth , out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length , _In_reads_bytes_opt_ ( out_buffer_length ) std : : basic_string < Char > & str , _Out_ sqlsrv_error_auto_ptr & last_error )
2017-01-25 02:19:18 +01:00
{
* out_buffer_length = str . size ( ) * sizeof ( Char ) ; // NULL terminator is provided subsequently
if ( * out_buffer_length > buffer_length ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " HY090 " , ( SQLCHAR * ) " Buffer length too small to hold number as string " , - 1 ) ;
return SQL_ERROR ;
}
memcpy_s ( buffer , * out_buffer_length , str . c_str ( ) , * out_buffer_length ) ;
return SQL_SUCCESS ;
}
# endif // !_WIN32
2016-01-30 03:00:20 +01:00
// convert a number to a string using locales
// There is an extra copy here, but given the size is short (usually <20 bytes) and the complications of
// subclassing a new streambuf just to avoid the copy, it's easier to do the copy
template < typename Char , typename Number >
2017-06-22 23:04:34 +02:00
SQLRETURN number_to_string ( _In_ Number * number_data , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length , _Inout_ SQLLEN * out_buffer_length , _Inout_ sqlsrv_error_auto_ptr & last_error )
2016-01-30 03:00:20 +01:00
{
2017-01-25 02:19:18 +01:00
size_t precision = 0 ;
2016-08-24 23:28:20 +02:00
2017-01-25 02:19:18 +01:00
# ifdef _WIN32
2016-01-30 03:00:20 +01:00
std : : basic_ostringstream < Char > os ;
2017-01-25 02:19:18 +01:00
precision = get_float_precision ( buffer_length , sizeof ( Char ) ) ;
os . precision ( precision ) ;
2016-01-30 03:00:20 +01:00
std : : locale loc ;
2017-01-25 02:19:18 +01:00
os . imbue ( loc ) ;
std : : use_facet < std : : num_put < Char > > ( loc ) . put ( std : : basic_ostream < Char > : : _Iter ( os . rdbuf ( ) ) , os , ' ' , * number_data ) ;
2016-01-30 03:00:20 +01:00
std : : basic_string < Char > & str_num = os . str ( ) ;
2017-01-25 02:19:18 +01:00
if ( os . fail ( ) ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " IMSSP " , ( SQLCHAR * ) " Failed to convert number to string " , - 1 ) ;
2016-01-30 03:00:20 +01:00
return SQL_ERROR ;
}
2017-01-25 02:19:18 +01:00
if ( str_num . size ( ) * sizeof ( Char ) > ( size_t ) buffer_length ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " HY090 " , ( SQLCHAR * ) " Buffer length too small to hold number as string " , - 1 ) ;
2016-01-30 03:00:20 +01:00
return SQL_ERROR ;
}
2017-01-25 02:19:18 +01:00
* out_buffer_length = str_num . size ( ) * sizeof ( Char ) ; // str_num.size() already include the NULL terminator
2016-07-06 20:47:05 +02:00
memcpy_s ( buffer , buffer_length , str_num . c_str ( ) , * out_buffer_length ) ;
2016-01-30 03:00:20 +01:00
return SQL_SUCCESS ;
2017-01-25 02:19:18 +01:00
# else
std : : basic_string < char > str_num ;
SQLRETURN r ;
if ( std : : is_integral < Number > : : value )
{
long num_data = * number_data ;
r = get_string_from_stream < long > ( num_data , str_num , precision , last_error ) ;
}
else
{
precision = get_float_precision ( buffer_length , sizeof ( Char ) ) ;
r = get_string_from_stream < double > ( * number_data , str_num , precision , last_error ) ;
}
if ( r = = SQL_ERROR ) return SQL_ERROR ;
if ( std : : is_same < Char , char16_t > : : value )
{
2017-02-21 03:11:27 +01:00
std : : basic_string < char16_t > str ;
for ( const auto & mb : str_num )
2017-01-25 02:19:18 +01:00
{
2017-02-21 03:11:27 +01:00
size_t cch = SystemLocale : : NextChar ( CP_ACP , & mb ) - & mb ;
if ( cch > 0 )
2017-01-25 02:19:18 +01:00
{
2017-02-21 03:11:27 +01:00
WCHAR ch16 ;
DWORD rc ;
size_t cchActual = SystemLocale : : ToUtf16 ( CP_ACP , & mb , cch , & ch16 , 1 , & rc ) ;
if ( cchActual > 0 )
2017-01-25 02:19:18 +01:00
{
2017-02-21 03:11:27 +01:00
str . push_back ( ch16 ) ;
2017-01-25 02:19:18 +01:00
}
}
}
return copy_buffer < char16_t > ( buffer , buffer_length , out_buffer_length , str , last_error ) ;
}
return copy_buffer < char > ( buffer , buffer_length , out_buffer_length , str_num , last_error ) ;
# endif // _WIN32
2016-01-30 03:00:20 +01:00
}
2017-02-22 03:47:53 +01:00
# ifndef _WIN32
2017-06-22 23:04:34 +02:00
std : : string getUTF8StringFromString ( _In_z_ const SQLWCHAR * source )
2017-02-22 03:47:53 +01:00
{
// convert to regular character string first
char c_str [ 4 ] = " " ;
SQLLEN i = 0 ;
std : : string str ;
while ( source [ i ] )
{
2017-02-22 20:51:31 +01:00
memset ( c_str , 0 , sizeof ( c_str ) ) ;
2017-02-22 03:47:53 +01:00
int cch = 0 ;
errno_t err = mplat_wctomb_s ( & cch , c_str , sizeof ( c_str ) , source [ i + + ] ) ;
2017-02-22 20:51:31 +01:00
if ( cch > 0 & & err = = ERROR_SUCCESS )
2017-02-22 03:47:53 +01:00
{
str . append ( std : : string ( c_str , cch ) ) ;
}
}
return str ;
}
2017-06-22 23:04:34 +02:00
std : : string getUTF8StringFromString ( _In_z_ const char * source )
2017-02-22 03:47:53 +01:00
{
return std : : string ( source ) ;
}
# endif // !_WIN32
2016-01-30 03:00:20 +01:00
// "closure" for the hash table destructor
struct row_dtor_closure {
sqlsrv_buffered_result_set * results ;
BYTE * row_data ;
2017-06-22 23:04:34 +02:00
row_dtor_closure ( _In_ sqlsrv_buffered_result_set * st , _In_ BYTE * row ) :
2016-01-30 03:00:20 +01:00
results ( st ) , row_data ( row )
{
}
} ;
2017-06-22 23:04:34 +02:00
sqlsrv_error * odbc_get_diag_rec ( _In_ sqlsrv_stmt * odbc , _In_ SQLSMALLINT record_number )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLWCHAR wsql_state [ SQL_SQLSTATE_BUFSIZE ] = { L ' \0 ' } ;
SQLWCHAR wnative_message [ SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ] = { L ' \0 ' } ;
2016-01-30 03:00:20 +01:00
SQLINTEGER native_code ;
SQLSMALLINT wnative_message_len = 0 ;
2018-06-06 22:18:58 +02:00
SQLSRV_ASSERT ( odbc ! = NULL , " odbc_get_diag_rec: sqlsrv_stmt* odbc was null. " ) ;
2016-01-30 03:00:20 +01:00
SQLRETURN r = SQLGetDiagRecW ( SQL_HANDLE_STMT , odbc - > handle ( ) , record_number , wsql_state , & native_code , wnative_message ,
2017-09-06 01:51:40 +02:00
SQL_MAX_ERROR_MESSAGE_LENGTH + 1 , & wnative_message_len ) ;
2016-01-30 03:00:20 +01:00
if ( ! SQL_SUCCEEDED ( r ) | | r = = SQL_NO_DATA ) {
return NULL ;
}
// convert the error into the encoding of the context
SQLSRV_ENCODING enc = odbc - > encoding ( ) ;
if ( enc = = SQLSRV_ENCODING_DEFAULT ) {
enc = odbc - > conn - > encoding ( ) ;
}
// convert the error into the encoding of the context
sqlsrv_malloc_auto_ptr < SQLCHAR > sql_state ;
2016-03-15 04:09:46 +01:00
SQLLEN sql_state_len = 0 ;
2017-01-25 02:19:18 +01:00
if ( ! convert_string_from_utf16 ( enc , wsql_state , SQL_SQLSTATE_BUFSIZE , ( char * * ) & sql_state , sql_state_len ) ) {
2016-01-30 03:00:20 +01:00
return NULL ;
}
sqlsrv_malloc_auto_ptr < SQLCHAR > native_message ;
2016-03-15 04:09:46 +01:00
SQLLEN native_message_len = 0 ;
2016-01-30 03:00:20 +01:00
if ( ! convert_string_from_utf16 ( enc , wnative_message , wnative_message_len , ( char * * ) & native_message , native_message_len ) ) {
return NULL ;
}
return new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) sql_state , ( SQLCHAR * ) native_message ,
native_code ) ;
}
} // namespace
// base class result set
2017-06-22 23:04:34 +02:00
sqlsrv_result_set : : sqlsrv_result_set ( _In_ sqlsrv_stmt * stmt ) :
2016-01-30 03:00:20 +01:00
odbc ( stmt )
{
}
// ODBC result set
// This object simply wraps ODBC function calls
2017-06-22 23:04:34 +02:00
sqlsrv_odbc_result_set : : sqlsrv_odbc_result_set ( _In_ sqlsrv_stmt * stmt ) :
2016-01-30 03:00:20 +01:00
sqlsrv_result_set ( stmt )
{
}
sqlsrv_odbc_result_set : : ~ sqlsrv_odbc_result_set ( void )
{
}
2020-04-21 00:17:21 +02:00
SQLRETURN sqlsrv_odbc_result_set : : fetch ( _In_ SQLSMALLINT orientation , _In_ SQLLEN offset )
2016-01-30 03:00:20 +01:00
{
SQLSRV_ASSERT ( odbc ! = NULL , " Invalid statement handle " ) ;
2020-04-21 00:17:21 +02:00
return core : : SQLFetchScroll ( odbc , orientation , offset ) ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_odbc_result_set : : get_data ( _In_ SQLUSMALLINT field_index , _In_ SQLSMALLINT target_type ,
_Out_writes_opt_ ( buffer_length ) SQLPOINTER buffer , _In_ SQLLEN buffer_length , _Inout_ SQLLEN * out_buffer_length ,
2020-04-21 00:17:21 +02:00
_In_ bool handle_warning )
2016-01-30 03:00:20 +01:00
{
SQLSRV_ASSERT ( odbc ! = NULL , " Invalid statement handle " ) ;
2020-04-21 00:17:21 +02:00
return core : : SQLGetData ( odbc , field_index , target_type , buffer , buffer_length , out_buffer_length , handle_warning ) ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_odbc_result_set : : get_diag_field ( _In_ SQLSMALLINT record_number , _In_ SQLSMALLINT diag_identifier ,
_Inout_updates_ ( buffer_length ) SQLPOINTER diag_info_buffer , _In_ SQLSMALLINT buffer_length ,
2020-04-21 00:17:21 +02:00
_Inout_ SQLSMALLINT * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
SQLSRV_ASSERT ( odbc ! = NULL , " Invalid statement handle " ) ;
return core : : SQLGetDiagField ( odbc , record_number , diag_identifier , diag_info_buffer , buffer_length ,
2020-04-21 00:17:21 +02:00
out_buffer_length ) ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
sqlsrv_error * sqlsrv_odbc_result_set : : get_diag_rec ( _In_ SQLSMALLINT record_number )
2016-01-30 03:00:20 +01:00
{
SQLSRV_ASSERT ( odbc ! = NULL , " Invalid statement handle " ) ;
return odbc_get_diag_rec ( odbc , record_number ) ;
}
2020-04-21 00:17:21 +02:00
SQLLEN sqlsrv_odbc_result_set : : row_count ( void )
2016-01-30 03:00:20 +01:00
{
SQLSRV_ASSERT ( odbc ! = NULL , " Invalid statement handle " ) ;
2020-04-21 00:17:21 +02:00
return core : : SQLRowCount ( odbc ) ;
2016-01-30 03:00:20 +01:00
}
// Buffered result set
// This class holds a result set in memory
2020-04-21 00:17:21 +02:00
sqlsrv_buffered_result_set : : sqlsrv_buffered_result_set ( _Inout_ sqlsrv_stmt * stmt ) :
2016-01-30 03:00:20 +01:00
sqlsrv_result_set ( stmt ) ,
cache ( NULL ) ,
col_count ( 0 ) ,
current ( 0 ) ,
last_field_index ( - 1 ) ,
2017-01-25 02:19:18 +01:00
read_so_far ( 0 ) ,
temp_length ( 0 )
2016-01-30 03:00:20 +01:00
{
2020-04-21 00:17:21 +02:00
col_count = core : : SQLNumResultCols ( stmt ) ;
2016-01-30 03:00:20 +01:00
// there is no result set to buffer
if ( col_count = = 0 ) {
return ;
}
SQLULEN null_bytes = ( col_count / 8 ) + 1 ; // number of bits to reserve at the beginning of each row for NULL flags
meta = static_cast < sqlsrv_buffered_result_set : : meta_data * > ( sqlsrv_malloc ( col_count *
sizeof ( sqlsrv_buffered_result_set : : meta_data ) ) ) ;
2016-12-13 00:39:50 +01:00
SQLSRV_ENCODING encoding = ( ( stmt - > encoding ( ) = = SQLSRV_ENCODING_DEFAULT ) ? stmt - > conn - > encoding ( ) :
stmt - > encoding ( ) ) ;
2016-01-30 03:00:20 +01:00
// get the meta data and calculate the size of a row buffer
SQLULEN offset = null_bytes ;
for ( SQLSMALLINT i = 0 ; i < col_count ; + + i ) {
2017-02-01 03:10:37 +01:00
2020-04-21 00:17:21 +02:00
core : : SQLDescribeColW ( stmt , i + 1 , NULL , 0 , NULL , & meta [ i ] . type , & meta [ i ] . length , & meta [ i ] . scale , NULL ) ;
2016-01-30 03:00:20 +01:00
2017-01-25 02:19:18 +01:00
offset = align_to < sizeof ( SQLPOINTER ) > ( offset ) ;
2016-01-30 03:00:20 +01:00
meta [ i ] . offset = offset ;
switch ( meta [ i ] . type ) {
// these types are the display size
case SQL_BIGINT :
case SQL_DECIMAL :
case SQL_GUID :
case SQL_NUMERIC :
2017-02-01 03:10:37 +01:00
core : : SQLColAttributeW ( stmt , i + 1 , SQL_DESC_DISPLAY_SIZE , NULL , 0 , NULL ,
2020-04-21 00:17:21 +02:00
reinterpret_cast < SQLLEN * > ( & meta [ i ] . length ) ) ;
2016-01-30 03:00:20 +01:00
meta [ i ] . length + = sizeof ( char ) + sizeof ( SQLULEN ) ; // null terminator space
offset + = meta [ i ] . length ;
break ;
2016-12-13 00:39:50 +01:00
case SQL_CHAR :
case SQL_VARCHAR :
2020-01-22 17:01:59 +01:00
case SQL_SS_VARIANT :
2016-12-13 00:39:50 +01:00
if ( meta [ i ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
offset + = sizeof ( void * ) ;
}
else {
// If encoding is set to UTF-8, the following types are not necessarily column size.
// We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly.
2016-12-13 02:57:03 +01:00
if ( encoding = = SQLSRV_ENCODING_UTF8 ) {
2016-12-13 00:39:50 +01:00
meta [ i ] . length * = sizeof ( WCHAR ) ;
meta [ i ] . length + = sizeof ( SQLULEN ) + sizeof ( WCHAR ) ; // length plus null terminator space
offset + = meta [ i ] . length ;
}
else {
meta [ i ] . length + = sizeof ( SQLULEN ) + sizeof ( char ) ; // length plus null terminator space
offset + = meta [ i ] . length ;
}
}
break ;
2016-01-30 03:00:20 +01:00
// these types are the column size
case SQL_BINARY :
case SQL_SS_UDT :
case SQL_VARBINARY :
// var* field types are length prefixed
if ( meta [ i ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
offset + = sizeof ( void * ) ;
}
else {
meta [ i ] . length + = sizeof ( SQLULEN ) + sizeof ( char ) ; // length plus null terminator space
offset + = meta [ i ] . length ;
}
break ;
case SQL_WCHAR :
case SQL_WVARCHAR :
if ( meta [ i ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
offset + = sizeof ( void * ) ;
}
else {
meta [ i ] . length * = sizeof ( WCHAR ) ;
meta [ i ] . length + = sizeof ( SQLULEN ) + sizeof ( WCHAR ) ; // length plus null terminator space
offset + = meta [ i ] . length ;
}
break ;
// these types are LOBs
case SQL_LONGVARBINARY :
case SQL_LONGVARCHAR :
case SQL_WLONGVARCHAR :
case SQL_SS_XML :
meta [ i ] . length = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ;
offset + = sizeof ( void * ) ;
break ;
// these types are the ISO date size
case SQL_DATETIME :
case SQL_TYPE_DATE :
case SQL_SS_TIME2 :
case SQL_SS_TIMESTAMPOFFSET :
case SQL_TYPE_TIMESTAMP :
2017-02-01 03:10:37 +01:00
core : : SQLColAttributeW ( stmt , i + 1 , SQL_DESC_DISPLAY_SIZE , NULL , 0 , NULL ,
2020-04-21 00:17:21 +02:00
reinterpret_cast < SQLLEN * > ( & meta [ i ] . length ) ) ;
2016-01-30 03:00:20 +01:00
meta [ i ] . length + = sizeof ( char ) + sizeof ( SQLULEN ) ; // null terminator space
offset + = meta [ i ] . length ;
break ;
// these types are the native size
case SQL_BIT :
case SQL_INTEGER :
case SQL_SMALLINT :
case SQL_TINYINT :
meta [ i ] . length = sizeof ( long ) ;
offset + = meta [ i ] . length ;
break ;
case SQL_REAL :
case SQL_FLOAT :
meta [ i ] . length = sizeof ( double ) ;
offset + = meta [ i ] . length ;
break ;
default :
SQLSRV_ASSERT ( false , " Unknown type in sqlsrv_buffered_query::sqlsrv_buffered_query " ) ;
break ;
}
switch ( meta [ i ] . type ) {
case SQL_BIGINT :
case SQL_DATETIME :
case SQL_DECIMAL :
case SQL_GUID :
case SQL_NUMERIC :
case SQL_TYPE_DATE :
case SQL_SS_TIME2 :
case SQL_SS_TIMESTAMPOFFSET :
case SQL_SS_XML :
case SQL_TYPE_TIMESTAMP :
meta [ i ] . c_type = SQL_C_CHAR ;
break ;
2016-12-13 01:04:12 +01:00
2016-12-13 00:39:50 +01:00
case SQL_CHAR :
case SQL_VARCHAR :
2020-01-22 17:01:59 +01:00
case SQL_SS_VARIANT :
2016-12-13 00:39:50 +01:00
case SQL_LONGVARCHAR :
// If encoding is set to UTF-8, the following types are not necessarily column size.
// We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly.
2016-12-13 02:57:03 +01:00
if ( encoding = = SQLSRV_ENCODING_UTF8 ) {
2016-12-13 00:39:50 +01:00
meta [ i ] . c_type = SQL_C_WCHAR ;
}
else {
meta [ i ] . c_type = SQL_C_CHAR ;
}
break ;
2016-12-13 01:04:12 +01:00
2016-01-30 03:00:20 +01:00
case SQL_SS_UDT :
case SQL_LONGVARBINARY :
case SQL_BINARY :
case SQL_VARBINARY :
meta [ i ] . c_type = SQL_C_BINARY ;
break ;
case SQL_WLONGVARCHAR :
case SQL_WCHAR :
case SQL_WVARCHAR :
meta [ i ] . c_type = SQL_C_WCHAR ;
break ;
case SQL_BIT :
case SQL_INTEGER :
case SQL_SMALLINT :
case SQL_TINYINT :
meta [ i ] . c_type = SQL_C_LONG ;
break ;
case SQL_REAL :
case SQL_FLOAT :
meta [ i ] . c_type = SQL_C_DOUBLE ;
break ;
default :
SQLSRV_ASSERT ( false , " Unknown type in sqlsrv_buffered_query::sqlsrv_buffered_query " ) ;
break ;
}
}
// read the data into the cache
// (offset from the above loop has the size of the row buffer necessary)
2016-04-12 23:43:46 +02:00
zend_long mem_used = 0 ;
2017-01-17 01:16:55 +01:00
size_t row_count = 0 ;
2017-02-24 22:29:09 +01:00
// 10 is an arbitrary number for now for the initial size of the cache
ALLOC_HASHTABLE ( cache ) ;
2020-04-21 00:17:21 +02:00
core : : sqlsrv_zend_hash_init ( * stmt , cache , 10 /* # of buckets */ , cache_row_dtor /*dtor*/ , 0 /*persistent*/ ) ;
2016-01-30 03:00:20 +01:00
2017-02-24 22:29:09 +01:00
try {
2020-04-21 00:17:21 +02:00
while ( core : : SQLFetchScroll ( stmt , SQL_FETCH_NEXT , 0 ) ! = SQL_NO_DATA ) {
2017-02-24 22:29:09 +01:00
// allocate the row buffer
sqlsrv_malloc_auto_ptr < unsigned char > rowAuto ;
rowAuto = static_cast < unsigned char * > ( sqlsrv_malloc ( offset ) ) ;
unsigned char * row = rowAuto . get ( ) ;
memset ( row , 0 , offset ) ;
// read the fields into the row buffer
for ( SQLSMALLINT i = 0 ; i < col_count ; + + i ) {
SQLLEN out_buffer_temp = SQL_NULL_DATA ;
SQLPOINTER buffer ;
SQLLEN * out_buffer_length = & out_buffer_temp ;
switch ( meta [ i ] . c_type ) {
case SQL_C_CHAR :
case SQL_C_WCHAR :
case SQL_C_BINARY :
if ( meta [ i ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
out_buffer_length = & out_buffer_temp ;
2018-08-01 02:22:56 +02:00
SQLPOINTER * lob_addr = reinterpret_cast < SQLPOINTER * > ( & row [ meta [ i ] . offset ] ) ;
2020-04-21 00:17:21 +02:00
* lob_addr = read_lob_field ( stmt , i , meta [ i ] , mem_used ) ;
2017-02-24 22:29:09 +01:00
// a NULL pointer means NULL field
if ( * lob_addr = = NULL ) {
* out_buffer_length = SQL_NULL_DATA ;
}
else {
* out_buffer_length = * * reinterpret_cast < SQLLEN * * > ( lob_addr ) ;
mem_used + = * out_buffer_length ;
}
2016-01-30 03:00:20 +01:00
}
else {
2017-02-24 22:29:09 +01:00
mem_used + = meta [ i ] . length ;
CHECK_CUSTOM_ERROR ( mem_used > stmt - > buffered_query_limit * 1024 , stmt ,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED , stmt - > buffered_query_limit ) {
2016-01-30 03:00:20 +01:00
2017-02-24 22:29:09 +01:00
throw core : : CoreException ( ) ;
}
2016-01-30 03:00:20 +01:00
2017-02-24 22:29:09 +01:00
buffer = row + meta [ i ] . offset + sizeof ( SQLULEN ) ;
out_buffer_length = reinterpret_cast < SQLLEN * > ( row + meta [ i ] . offset ) ;
core : : SQLGetData ( stmt , i + 1 , meta [ i ] . c_type , buffer , meta [ i ] . length , out_buffer_length ,
2020-04-21 00:17:21 +02:00
false ) ;
2016-01-30 03:00:20 +01:00
}
2017-02-24 22:29:09 +01:00
break ;
case SQL_C_LONG :
case SQL_C_DOUBLE :
{
mem_used + = meta [ i ] . length ;
CHECK_CUSTOM_ERROR ( mem_used > stmt - > buffered_query_limit * 1024 , stmt ,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED , stmt - > buffered_query_limit ) {
throw core : : CoreException ( ) ;
}
buffer = row + meta [ i ] . offset ;
out_buffer_length = & out_buffer_temp ;
core : : SQLGetData ( stmt , i + 1 , meta [ i ] . c_type , buffer , meta [ i ] . length , out_buffer_length ,
2020-04-21 00:17:21 +02:00
false ) ;
2017-02-24 22:29:09 +01:00
}
break ;
2016-01-30 03:00:20 +01:00
2017-02-24 22:29:09 +01:00
default :
SQLSRV_ASSERT ( false , " Unknown C type " ) ;
break ;
}
2016-01-30 03:00:20 +01:00
2017-02-24 22:29:09 +01:00
if ( * out_buffer_length = = SQL_NULL_DATA ) {
set_bit ( row , i ) ;
}
2016-01-30 03:00:20 +01:00
}
2017-08-08 21:03:51 +02:00
row_count + + ;
2017-02-24 22:29:09 +01:00
SQLSRV_ASSERT ( row_count < INT_MAX , " Hard maximum of 2 billion rows exceeded in a buffered query " ) ;
// add it to the cache
row_dtor_closure cl ( this , row ) ;
2020-04-21 00:17:21 +02:00
sqlsrv_zend_hash_next_index_insert_mem ( * stmt , cache , & cl , sizeof ( row_dtor_closure ) ) ;
2017-02-24 22:29:09 +01:00
rowAuto . transferred ( ) ;
}
}
catch ( core : : CoreException & ) {
// free the rows
if ( cache ) {
zend_hash_destroy ( cache ) ;
FREE_HASHTABLE ( cache ) ;
cache = NULL ;
}
throw ;
2016-01-30 03:00:20 +01:00
}
}
sqlsrv_buffered_result_set : : ~ sqlsrv_buffered_result_set ( void )
{
// free the rows
if ( cache ) {
zend_hash_destroy ( cache ) ;
FREE_HASHTABLE ( cache ) ;
cache = NULL ;
}
}
2020-04-21 00:17:21 +02:00
SQLRETURN sqlsrv_buffered_result_set : : fetch ( _Inout_ SQLSMALLINT orientation , _Inout_opt_ SQLLEN offset )
2016-01-30 03:00:20 +01:00
{
last_error = NULL ;
last_field_index = - 1 ;
read_so_far = 0 ;
switch ( orientation ) {
case SQL_FETCH_NEXT :
offset = 1 ;
orientation = SQL_FETCH_RELATIVE ;
break ;
case SQL_FETCH_PRIOR :
offset = - 1 ;
orientation = SQL_FETCH_RELATIVE ;
break ;
}
switch ( orientation ) {
case SQL_FETCH_FIRST :
current = 1 ;
break ;
case SQL_FETCH_LAST :
2020-04-21 00:17:21 +02:00
current = row_count ( ) ;
2016-01-30 03:00:20 +01:00
break ;
case SQL_FETCH_ABSOLUTE :
current = offset ;
break ;
case SQL_FETCH_RELATIVE :
current + = offset ;
break ;
default :
SQLSRV_ASSERT ( false , " Invalid fetch orientation. Should have been caught before here. " ) ;
break ;
}
// check validity of current row
// the cursor can never get further away than just before the first row
if ( current < = 0 & & ( offset < 0 | | orientation ! = SQL_FETCH_RELATIVE ) ) {
current = 0 ;
return SQL_NO_DATA ;
}
// the cursor can never get further away than just after the last row
2020-04-21 00:17:21 +02:00
if ( current > row_count ( ) | | ( current < = 0 & & offset > 0 ) /*overflow condition*/ ) {
current = row_count ( ) + 1 ;
2016-01-30 03:00:20 +01:00
return SQL_NO_DATA ;
}
return SQL_SUCCESS ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : get_data ( _In_ SQLUSMALLINT field_index , _In_ SQLSMALLINT target_type ,
_Out_writes_bytes_opt_ ( buffer_length ) SQLPOINTER buffer , _In_ SQLLEN buffer_length , _Inout_ SQLLEN * out_buffer_length ,
2020-04-21 00:17:21 +02:00
bool handle_warning )
2016-01-30 03:00:20 +01:00
{
last_error = NULL ;
field_index - - ; // convert from 1 based to 0 based
SQLSRV_ASSERT ( field_index < column_count ( ) , " Invalid field index requested " ) ;
if ( field_index ! = last_field_index ) {
last_field_index = field_index ;
read_so_far = 0 ;
}
unsigned char * row = get_row ( ) ;
// if the field is null, then return SQL_NULL_DATA
if ( get_bit ( row , field_index ) ) {
* out_buffer_length = SQL_NULL_DATA ;
return SQL_SUCCESS ;
}
2017-01-25 02:19:18 +01:00
2016-01-30 03:00:20 +01:00
// check to make sure the conversion type is valid
2020-02-19 23:27:36 +01:00
switch ( meta [ field_index ] . c_type ) {
case SQL_C_CHAR :
switch ( target_type ) {
case SQL_C_CHAR : return sqlsrv_buffered_result_set : : to_same_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_WCHAR : return sqlsrv_buffered_result_set : : system_to_wide_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_BINARY : return sqlsrv_buffered_result_set : : to_binary_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_DOUBLE : return sqlsrv_buffered_result_set : : string_to_double ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_LONG : return sqlsrv_buffered_result_set : : string_to_long ( field_index , buffer , buffer_length , out_buffer_length ) ;
default :
break ;
}
break ;
case SQL_C_WCHAR :
switch ( target_type ) {
case SQL_C_WCHAR : return sqlsrv_buffered_result_set : : to_same_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_BINARY : return sqlsrv_buffered_result_set : : to_binary_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_CHAR : return sqlsrv_buffered_result_set : : wide_to_system_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_DOUBLE : return sqlsrv_buffered_result_set : : wstring_to_double ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_LONG : return sqlsrv_buffered_result_set : : wstring_to_long ( field_index , buffer , buffer_length , out_buffer_length ) ;
default :
break ;
}
break ;
case SQL_C_BINARY :
switch ( target_type ) {
case SQL_C_BINARY : return sqlsrv_buffered_result_set : : to_same_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_CHAR : return sqlsrv_buffered_result_set : : binary_to_system_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_WCHAR : return sqlsrv_buffered_result_set : : binary_to_wide_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
default :
break ;
}
break ;
case SQL_C_LONG :
switch ( target_type ) {
case SQL_C_DOUBLE : return sqlsrv_buffered_result_set : : long_to_double ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_LONG : return sqlsrv_buffered_result_set : : to_long ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_BINARY : return sqlsrv_buffered_result_set : : to_long ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_CHAR : return sqlsrv_buffered_result_set : : long_to_system_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
default :
break ;
}
break ;
case SQL_C_DOUBLE :
switch ( target_type ) {
case SQL_C_DOUBLE : return sqlsrv_buffered_result_set : : to_double ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_BINARY : return sqlsrv_buffered_result_set : : to_double ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_CHAR : return sqlsrv_buffered_result_set : : double_to_system_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
case SQL_C_LONG : return sqlsrv_buffered_result_set : : double_to_long ( field_index , buffer , buffer_length , out_buffer_length ) ;
default :
break ;
}
break ;
default :
break ;
2016-01-30 03:00:20 +01:00
}
2020-02-19 23:27:36 +01:00
// Should not have reached here, return an error
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) )
sqlsrv_error ( ( SQLCHAR * ) " 07006 " , ( SQLCHAR * ) " Restricted data type attribute violation " , 0 ) ;
return SQL_ERROR ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : get_diag_field ( _In_ SQLSMALLINT record_number , _In_ SQLSMALLINT diag_identifier ,
_Inout_updates_ ( buffer_length ) SQLPOINTER diag_info_buffer , _In_ SQLSMALLINT buffer_length ,
2020-04-21 00:17:21 +02:00
_Inout_ SQLSMALLINT * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
SQLSRV_ASSERT ( record_number = = 1 , " Only record number 1 can be fetched by sqlsrv_buffered_result_set::get_diag_field " ) ;
SQLSRV_ASSERT ( diag_identifier = = SQL_DIAG_SQLSTATE ,
" Only SQL_DIAG_SQLSTATE can be fetched by sqlsrv_buffered_result_set::get_diag_field " ) ;
SQLSRV_ASSERT ( buffer_length > = SQL_SQLSTATE_BUFSIZE ,
" Buffer not big enough to return SQLSTATE in sqlsrv_buffered_result_set::get_diag_field " ) ;
2017-01-25 02:19:18 +01:00
if ( last_error = = 0 ) {
2016-01-30 03:00:20 +01:00
return SQL_NO_DATA ;
}
SQLSRV_ASSERT ( last_error - > sqlstate ! = NULL ,
" Must have a SQLSTATE in a valid last_error in sqlsrv_buffered_result_set::get_diag_field " ) ;
2017-01-25 02:19:18 +01:00
SQLSMALLINT bufsize = ( buffer_length < SQL_SQLSTATE_BUFSIZE ) ? buffer_length : SQL_SQLSTATE_BUFSIZE ;
memcpy_s ( diag_info_buffer , buffer_length , last_error - > sqlstate , bufsize ) ;
2016-01-30 03:00:20 +01:00
return SQL_SUCCESS ;
}
unsigned char * sqlsrv_buffered_result_set : : get_row ( void )
{
row_dtor_closure * cl_ptr ;
cl_ptr = reinterpret_cast < row_dtor_closure * > ( zend_hash_index_find_ptr ( cache , static_cast < zend_ulong > ( current - 1 ) ) ) ;
SQLSRV_ASSERT ( cl_ptr ! = NULL , " Failed to find row %1!d! in the cache " , current ) ;
return cl_ptr - > row_data ;
}
2017-06-22 23:04:34 +02:00
sqlsrv_error * sqlsrv_buffered_result_set : : get_diag_rec ( _In_ SQLSMALLINT record_number )
2016-01-30 03:00:20 +01:00
{
// we only hold a single error if there is one, otherwise return the ODBC error(s)
2017-01-25 02:19:18 +01:00
if ( last_error = = 0 ) {
2016-01-30 03:00:20 +01:00
return odbc_get_diag_rec ( odbc , record_number ) ;
}
if ( record_number > 1 ) {
return NULL ;
}
return new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) )
sqlsrv_error ( last_error - > sqlstate , last_error - > native_message , last_error - > native_code ) ;
}
2020-04-21 00:17:21 +02:00
SQLLEN sqlsrv_buffered_result_set : : row_count ( void )
2016-01-30 03:00:20 +01:00
{
last_error = NULL ;
2017-03-20 23:58:57 +01:00
if ( cache ) {
return zend_hash_num_elements ( cache ) ;
}
else {
2017-03-23 18:52:02 +01:00
// returning -1 to represent getting the rowcount of an empty result set
2017-03-20 23:58:57 +01:00
return - 1 ;
}
2016-01-30 03:00:20 +01:00
}
// private functions
template < typename Char >
2017-06-22 23:04:34 +02:00
SQLRETURN binary_to_string ( _Inout_ SQLCHAR * field_data , _Inout_ SQLLEN & read_so_far , _Out_writes_z_ ( * out_buffer_length ) void * buffer ,
_In_ SQLLEN buffer_length , _Inout_ SQLLEN * out_buffer_length ,
_Inout_ sqlsrv_error_auto_ptr & out_error )
2016-01-30 03:00:20 +01:00
{
// hex characters for the conversion loop below
static char hex_chars [ ] = " 0123456789ABCDEF " ;
2017-01-25 02:19:18 +01:00
SQLSRV_ASSERT ( out_error = = 0 , " Pending error for sqlsrv_buffered_results_set::binary_to_string " ) ;
2016-01-30 03:00:20 +01:00
SQLRETURN r = SQL_ERROR ;
// Set the amount of space necessary for null characters at the end of the data.
SQLSMALLINT extra = sizeof ( Char ) ;
SQLSRV_ASSERT ( ( ( buffer_length - extra ) % ( extra * 2 ) ) = = 0 , " Must be multiple of 2 for binary to system string or "
" multiple of 4 for binary to wide string " ) ;
// all fields will be treated as ODBC returns varchar(max) fields:
// the entire length of the string is returned the first
// call in out_buffer_len. Successive calls return how much is
// left minus how much has already been read by previous reads
// *2 is for each byte to hex conversion and * extra is for either system or wide string allocation
* out_buffer_length = ( * reinterpret_cast < SQLLEN * > ( field_data - sizeof ( SQLULEN ) ) - read_so_far ) * 2 * extra ;
// copy as much as we can into the buffer
SQLLEN to_copy ;
if ( buffer_length < * out_buffer_length + extra ) {
to_copy = ( buffer_length - extra ) ;
out_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) )
sqlsrv_error ( ( SQLCHAR * ) " 01004 " , ( SQLCHAR * ) " String data, right truncated " , - 1 ) ;
r = SQL_SUCCESS_WITH_INFO ;
}
else {
r = SQL_SUCCESS ;
to_copy = * out_buffer_length ;
}
// if there are bytes to copy as hex
if ( to_copy > 0 ) {
// quick hex conversion routine
Char * h = reinterpret_cast < Char * > ( buffer ) ;
BYTE * b = reinterpret_cast < BYTE * > ( field_data ) ;
// to_copy contains the number of bytes to copy, so we divide the number in half (or quarter)
// to get the number of hex digits we can copy
SQLLEN to_copy_hex = to_copy / ( 2 * extra ) ;
2018-06-27 20:03:30 +02:00
for ( SQLLEN i = 0 ; i < to_copy_hex ; + + i ) {
2018-08-01 02:22:56 +02:00
* h = hex_chars [ ( * b & 0xf0 ) > > 4 ] ;
2016-01-30 03:00:20 +01:00
h + + ;
2018-08-01 02:22:56 +02:00
* h = hex_chars [ ( * b + + & 0x0f ) ] ;
2016-01-30 03:00:20 +01:00
h + + ;
}
read_so_far + = to_copy_hex ;
* h = static_cast < Char > ( 0 ) ;
}
else {
reinterpret_cast < char * > ( buffer ) [ 0 ] = ' \0 ' ;
}
return r ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : binary_to_system_string ( _In_ SQLSMALLINT field_index , _Out_writes_z_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
SQLCHAR * row = get_row ( ) ;
SQLCHAR * field_data = NULL ;
2018-08-01 02:22:56 +02:00
if ( meta [ field_index ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
2016-01-30 03:00:20 +01:00
2018-08-01 02:22:56 +02:00
field_data = * reinterpret_cast < SQLCHAR * * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
}
else {
2018-08-01 02:22:56 +02:00
field_data = & row [ meta [ field_index ] . offset ] + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
}
return binary_to_string < char > ( field_data , read_so_far , buffer , buffer_length , out_buffer_length , last_error ) ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : binary_to_wide_string ( _In_ SQLSMALLINT field_index , _Out_writes_z_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
SQLCHAR * row = get_row ( ) ;
SQLCHAR * field_data = NULL ;
2018-08-01 02:22:56 +02:00
if ( meta [ field_index ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
2016-01-30 03:00:20 +01:00
2018-08-01 02:22:56 +02:00
field_data = * reinterpret_cast < SQLCHAR * * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
}
else {
2018-08-01 02:22:56 +02:00
field_data = & row [ meta [ field_index ] . offset ] + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
}
return binary_to_string < WCHAR > ( field_data , read_so_far , buffer , buffer_length , out_buffer_length , last_error ) ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : double_to_long ( _In_ SQLSMALLINT field_index , _Inout_updates_bytes_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_DOUBLE , " Invalid conversion to long " ) ;
2016-03-15 04:09:46 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( SQLLEN ) , " Buffer length must be able to find a long in "
2016-01-30 03:00:20 +01:00
" sqlsrv_buffered_result_set::double_to_long " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
double * double_data = reinterpret_cast < double * > ( & row [ meta [ field_index ] . offset ] ) ;
2016-01-30 03:00:20 +01:00
LONG * long_data = reinterpret_cast < LONG * > ( buffer ) ;
if ( * double_data < double ( LONG_MIN ) | | * double_data > double ( LONG_MAX ) ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " 22003 " ,
( SQLCHAR * ) " Numeric value out of range " , 0 ) ;
return SQL_ERROR ;
}
if ( * double_data ! = floor ( * double_data ) ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " 01S07 " ,
( SQLCHAR * ) " Fractional truncation " , 0 ) ;
return SQL_SUCCESS_WITH_INFO ;
}
* long_data = static_cast < LONG > ( * double_data ) ;
* out_buffer_length = sizeof ( LONG ) ;
return SQL_SUCCESS ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : double_to_system_string ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_DOUBLE , " Invalid conversion to system string " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > 0 , " Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_system_string " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
double * double_data = reinterpret_cast < double * > ( & row [ meta [ field_index ] . offset ] ) ;
2017-01-25 02:19:18 +01:00
SQLRETURN r = SQL_SUCCESS ;
# ifdef _WIN32
r = number_to_string < char > ( double_data , buffer , buffer_length , out_buffer_length , last_error ) ;
# else
r = number_to_string < char , double > ( double_data , buffer , buffer_length , out_buffer_length , last_error ) ;
# endif // _WIN32
return r ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : long_to_double ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
2016-07-06 20:47:05 +02:00
_Out_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_LONG , " Invalid conversion to long " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( double ) , " Buffer length must be able to find a long in sqlsrv_buffered_result_set::double_to_long " ) ;
unsigned char * row = get_row ( ) ;
double * double_data = reinterpret_cast < double * > ( buffer ) ;
2018-08-01 02:22:56 +02:00
LONG * long_data = reinterpret_cast < LONG * > ( & row [ meta [ field_index ] . offset ] ) ;
2016-01-30 03:00:20 +01:00
* double_data = static_cast < LONG > ( * long_data ) ;
* out_buffer_length = sizeof ( double ) ;
return SQL_SUCCESS ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : long_to_system_string ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_LONG , " Invalid conversion to system string " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > 0 , " Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_system_string " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
LONG * long_data = reinterpret_cast < LONG * > ( & row [ meta [ field_index ] . offset ] ) ;
2017-01-25 02:19:18 +01:00
SQLRETURN r = SQL_SUCCESS ;
# ifdef _WIN32
r = number_to_string < char > ( long_data , buffer , buffer_length , out_buffer_length , last_error ) ;
# else
r = number_to_string < char , LONG > ( long_data , buffer , buffer_length , out_buffer_length , last_error ) ;
# endif // _WIN32
return r ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : string_to_double ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_CHAR , " Invalid conversion from string to double " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( double ) , " Buffer needs to be big enough to hold a double " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
char * string_data = reinterpret_cast < char * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
2020-06-27 00:56:51 +02:00
double * number_data = reinterpret_cast < double * > ( buffer ) ;
try {
* number_data = std : : stod ( std : : string ( string_data ) ) ;
} catch ( const std : : logic_error & err ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " 22003 " , ( SQLCHAR * ) " Numeric value out of range " , 103 ) ;
return SQL_ERROR ;
}
* out_buffer_length = sizeof ( double ) ;
return SQL_SUCCESS ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : wstring_to_double ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_WCHAR , " Invalid conversion from wide string to double " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( double ) , " Buffer needs to be big enough to hold a double " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
SQLWCHAR * string_data = reinterpret_cast < SQLWCHAR * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) / sizeof ( SQLWCHAR ) ;
2016-01-30 03:00:20 +01:00
2020-06-27 00:56:51 +02:00
double * number_data = reinterpret_cast < double * > ( buffer ) ;
try {
# ifdef _WIN32
* number_data = std : : stod ( std : : wstring ( string_data ) ) ;
# else
* number_data = std : : stod ( getUTF8StringFromString ( string_data ) ) ;
# endif // _WIN32
} catch ( const std : : logic_error & err ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " 22003 " , ( SQLCHAR * ) " Numeric value out of range " , 103 ) ;
return SQL_ERROR ;
}
* out_buffer_length = sizeof ( double ) ;
return SQL_SUCCESS ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : string_to_long ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_CHAR , " Invalid conversion from string to long " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( LONG ) , " Buffer needs to be big enough to hold a long " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
char * string_data = reinterpret_cast < char * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
2020-06-27 00:56:51 +02:00
LONG * number_data = reinterpret_cast < LONG * > ( buffer ) ;
try {
* number_data = std : : stol ( std : : string ( string_data ) ) ;
} catch ( const std : : logic_error & err ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " 22003 " , ( SQLCHAR * ) " Numeric value out of range " , 103 ) ;
return SQL_ERROR ;
}
* out_buffer_length = sizeof ( LONG ) ;
return SQL_SUCCESS ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : wstring_to_long ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
_Inout_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_WCHAR , " Invalid conversion from wide string to long " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( LONG ) , " Buffer needs to be big enough to hold a long " ) ;
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
SQLWCHAR * string_data = reinterpret_cast < SQLWCHAR * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) / sizeof ( SQLWCHAR ) ;
2016-01-30 03:00:20 +01:00
2020-06-27 00:56:51 +02:00
LONG * number_data = reinterpret_cast < LONG * > ( buffer ) ;
try {
# ifdef _WIN32
* number_data = std : : stol ( std : : wstring ( string_data ) ) ;
# else
* number_data = std : : stol ( getUTF8StringFromString ( string_data ) ) ;
# endif // _WIN32
} catch ( const std : : logic_error & err ) {
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) ) sqlsrv_error ( ( SQLCHAR * ) " 22003 " , ( SQLCHAR * ) " Numeric value out of range " , 103 ) ;
return SQL_ERROR ;
}
* out_buffer_length = sizeof ( LONG ) ;
return SQL_SUCCESS ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : system_to_wide_string ( _In_ SQLSMALLINT field_index , _Out_writes_z_ ( * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
2016-07-06 20:47:05 +02:00
_Out_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2017-01-25 02:19:18 +01:00
SQLSRV_ASSERT ( last_error = = 0 , " Pending error for sqlsrv_buffered_results_set::system_to_wide_string " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length % 2 = = 0 , " Odd buffer length passed to sqlsrv_buffered_result_set::system_to_wide_string " ) ;
SQLRETURN r = SQL_ERROR ;
unsigned char * row = get_row ( ) ;
SQLCHAR * field_data = NULL ;
2017-01-25 02:19:18 +01:00
SQLULEN field_len = 0 ;
2016-01-30 03:00:20 +01:00
2018-08-01 02:22:56 +02:00
if ( meta [ field_index ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
2016-01-30 03:00:20 +01:00
2018-08-01 02:22:56 +02:00
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 ;
2016-01-30 03:00:20 +01:00
}
else {
2018-08-01 02:22:56 +02:00
field_len = * reinterpret_cast < SQLLEN * > ( & row [ meta [ field_index ] . offset ] ) ;
field_data = & row [ meta [ field_index ] . offset ] + sizeof ( SQLULEN ) + read_so_far ;
2016-01-30 03:00:20 +01:00
}
// all fields will be treated as ODBC returns varchar(max) fields:
// the entire length of the string is returned the first
// call in out_buffer_len. Successive calls return how much is
// left minus how much has already been read by previous reads
* out_buffer_length = ( * reinterpret_cast < SQLLEN * > ( field_data - sizeof ( SQLULEN ) ) - read_so_far ) * sizeof ( WCHAR ) ;
// to_copy is the number of characters to copy, not including the null terminator
// supposedly it will never happen that a Windows MBCS will explode to UTF-16 surrogate pair.
SQLLEN to_copy ;
if ( ( size_t ) buffer_length < ( field_len - read_so_far + sizeof ( char ) ) * sizeof ( WCHAR ) ) {
to_copy = ( buffer_length - sizeof ( WCHAR ) ) / sizeof ( WCHAR ) ; // to_copy is the number of characters
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) )
sqlsrv_error ( ( SQLCHAR * ) " 01004 " , ( SQLCHAR * ) " String data, right truncated " , - 1 ) ;
r = SQL_SUCCESS_WITH_INFO ;
}
else {
r = SQL_SUCCESS ;
to_copy = field_len - read_so_far ;
}
if ( to_copy > 0 ) {
bool tried_again = false ;
do {
2016-03-15 04:09:46 +01:00
if ( to_copy > INT_MAX ) {
LOG ( SEV_ERROR , " MultiByteToWideChar: Buffer length exceeded. " ) ;
throw core : : CoreException ( ) ;
}
2017-01-27 02:06:17 +01:00
# ifndef _WIN32
2017-01-25 02:19:18 +01:00
int ch_space = SystemLocale : : ToUtf16 ( CP_ACP , ( LPCSTR ) field_data , static_cast < int > ( to_copy ) ,
static_cast < LPWSTR > ( buffer ) , static_cast < int > ( to_copy ) ) ;
# else
2016-03-15 04:09:46 +01:00
int ch_space = MultiByteToWideChar ( CP_ACP , MB_ERR_INVALID_CHARS , ( LPCSTR ) field_data , static_cast < int > ( to_copy ) ,
static_cast < LPWSTR > ( buffer ) , static_cast < int > ( to_copy ) ) ;
2017-01-27 02:06:17 +01:00
# endif // !_WIN32
2017-01-25 02:19:18 +01:00
2016-01-30 03:00:20 +01:00
if ( ch_space = = 0 ) {
switch ( GetLastError ( ) ) {
case ERROR_NO_UNICODE_TRANSLATION :
// the theory here is the conversion failed because the end of the buffer we provided contained only
// half a character at the end
if ( ! tried_again ) {
to_copy - - ;
tried_again = true ;
continue ;
}
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 ;
}
2018-08-01 02:22:56 +02:00
( ( WCHAR * ) buffer ) [ to_copy ] = L ' \0 ' ;
2016-01-30 03:00:20 +01:00
read_so_far + = to_copy ;
break ;
} while ( true ) ;
}
else {
reinterpret_cast < WCHAR * > ( buffer ) [ 0 ] = L ' \0 ' ;
}
return r ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : to_same_string ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
2016-07-06 20:47:05 +02:00
_Out_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2017-01-25 02:19:18 +01:00
SQLSRV_ASSERT ( last_error = = 0 , " Pending error for sqlsrv_buffered_results_set::to_same_string " ) ;
2016-01-30 03:00:20 +01:00
SQLRETURN r = SQL_ERROR ;
unsigned char * row = get_row ( ) ;
// Set the amount of space necessary for null characters at the end of the data.
SQLSMALLINT extra = 0 ;
2018-08-01 02:22:56 +02:00
switch ( meta [ field_index ] . c_type ) {
2016-01-30 03:00:20 +01:00
case SQL_C_WCHAR :
extra = sizeof ( SQLWCHAR ) ;
break ;
case SQL_C_BINARY :
extra = 0 ;
break ;
case SQL_C_CHAR :
extra = sizeof ( SQLCHAR ) ;
break ;
default :
SQLSRV_ASSERT ( false , " Invalid type in get_string_data " ) ;
break ;
}
SQLCHAR * field_data = NULL ;
2018-08-01 02:22:56 +02:00
if ( meta [ field_index ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
2016-01-30 03:00:20 +01:00
2018-08-01 02:22:56 +02:00
field_data = * reinterpret_cast < SQLCHAR * * > ( & row [ meta [ field_index ] . offset ] ) + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
}
else {
2018-08-01 02:22:56 +02:00
field_data = & row [ meta [ field_index ] . offset ] + sizeof ( SQLULEN ) ;
2016-01-30 03:00:20 +01:00
}
// all fields will be treated as ODBC returns varchar(max) fields:
// the entire length of the string is returned the first
// call in out_buffer_len. Successive calls return how much is
// left minus how much has already been read by previous reads
* out_buffer_length = * reinterpret_cast < SQLLEN * > ( field_data - sizeof ( SQLULEN ) ) - read_so_far ;
// copy as much as we can into the buffer
SQLLEN to_copy ;
if ( buffer_length < * out_buffer_length + extra ) {
to_copy = buffer_length - extra ;
last_error = new ( sqlsrv_malloc ( sizeof ( sqlsrv_error ) ) )
sqlsrv_error ( ( SQLCHAR * ) " 01004 " , ( SQLCHAR * ) " String data, right truncated " , - 1 ) ;
r = SQL_SUCCESS_WITH_INFO ;
}
else {
r = SQL_SUCCESS ;
to_copy = * out_buffer_length ;
}
SQLSRV_ASSERT ( to_copy > = 0 , " Negative field length calculated in buffered result set " ) ;
if ( to_copy > 0 ) {
2016-07-06 20:47:05 +02:00
memcpy_s ( buffer , buffer_length , field_data + read_so_far , to_copy ) ;
2016-01-30 03:00:20 +01:00
read_so_far + = to_copy ;
}
if ( extra ) {
OACR_WARNING_SUPPRESS ( 26001 , " Buffer length verified above " ) ;
2016-07-06 20:47:05 +02:00
memcpy_s ( reinterpret_cast < SQLCHAR * > ( buffer ) + to_copy , buffer_length , L " \0 " , extra ) ;
2016-01-30 03:00:20 +01:00
}
return r ;
}
2017-06-22 23:04:34 +02:00
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 )
2016-01-30 03:00:20 +01:00
{
2017-01-25 02:19:18 +01:00
SQLSRV_ASSERT ( last_error = = 0 , " Pending error for sqlsrv_buffered_results_set::wide_to_system_string " ) ;
2016-01-30 03:00:20 +01:00
SQLRETURN r = SQL_ERROR ;
unsigned char * row = get_row ( ) ;
SQLCHAR * field_data = NULL ;
2017-01-25 02:19:18 +01:00
SQLLEN field_len = 0 ;
2016-01-30 03:00:20 +01:00
// 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 ) {
2018-08-01 02:22:56 +02:00
if ( meta [ field_index ] . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
2016-01-30 03:00:20 +01:00
2018-08-01 02:22:56 +02:00
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 ;
2016-01-30 03:00:20 +01:00
}
else {
2018-08-01 02:22:56 +02:00
field_len = * reinterpret_cast < SQLLEN * > ( & row [ meta [ field_index ] . offset ] ) ;
field_data = & row [ meta [ field_index ] . offset ] + sizeof ( SQLULEN ) + read_so_far ;
2016-01-30 03:00:20 +01:00
}
2017-02-10 19:22:16 +01:00
if ( field_len = = 0 ) { // empty string, no need for conversion
2017-02-10 18:51:51 +01:00
* out_buffer_length = 0 ;
return SQL_SUCCESS ;
}
2017-01-25 02:19:18 +01:00
// allocate enough to handle WC -> DBCS conversion if it happens
temp_string = reinterpret_cast < SQLCHAR * > ( sqlsrv_malloc ( field_len , sizeof ( char ) , sizeof ( char ) ) ) ;
2017-01-27 02:06:17 +01:00
# 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 ) ) ;
2017-01-25 02:19:18 +01:00
# else
2016-01-30 03:00:20 +01:00
BOOL default_char_used = FALSE ;
char default_char = ' ? ' ;
2016-03-15 04:09:46 +01:00
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 ) ;
2017-01-27 02:06:17 +01:00
# endif // !_WIN32
2016-01-30 03:00:20 +01:00
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 ) {
2016-07-06 20:47:05 +02:00
memcpy_s ( buffer , buffer_length , temp_string . get ( ) + read_so_far , to_copy ) ;
2016-01-30 03:00:20 +01:00
}
SQLSRV_ASSERT ( to_copy > = 0 , " Invalid field copy length " ) ;
OACR_WARNING_SUPPRESS ( BUFFER_UNDERFLOW , " Buffer length verified above " ) ;
2018-08-01 02:22:56 +02:00
( ( SQLCHAR * ) buffer ) [ to_copy ] = ' \0 ' ;
2016-01-30 03:00:20 +01:00
read_so_far + = to_copy ;
return r ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : to_binary_string ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
2016-07-06 20:47:05 +02:00
_Out_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
return to_same_string ( field_index , buffer , buffer_length , out_buffer_length ) ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : to_long ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
2016-07-06 20:47:05 +02:00
_Out_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_LONG , " Invalid conversion to long " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( LONG ) , " Buffer too small for SQL_C_LONG " ) ; // technically should ignore this
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
LONG * long_data = reinterpret_cast < LONG * > ( & row [ meta [ field_index ] . offset ] ) ;
2016-07-06 20:47:05 +02:00
memcpy_s ( buffer , buffer_length , long_data , sizeof ( LONG ) ) ;
2016-01-30 03:00:20 +01:00
* out_buffer_length = sizeof ( LONG ) ;
return SQL_SUCCESS ;
}
2017-06-22 23:04:34 +02:00
SQLRETURN sqlsrv_buffered_result_set : : to_double ( _In_ SQLSMALLINT field_index , _Out_writes_bytes_to_opt_ ( buffer_length , * out_buffer_length ) void * buffer , _In_ SQLLEN buffer_length ,
2016-07-06 20:47:05 +02:00
_Out_ SQLLEN * out_buffer_length )
2016-01-30 03:00:20 +01:00
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( meta [ field_index ] . c_type = = SQL_C_DOUBLE , " Invalid conversion to double " ) ;
2016-01-30 03:00:20 +01:00
SQLSRV_ASSERT ( buffer_length > = sizeof ( double ) , " Buffer too small for SQL_C_DOUBLE " ) ; // technically should ignore this
unsigned char * row = get_row ( ) ;
2018-08-01 02:22:56 +02:00
double * double_data = reinterpret_cast < double * > ( & row [ meta [ field_index ] . offset ] ) ;
2016-07-06 20:47:05 +02:00
memcpy_s ( buffer , buffer_length , double_data , sizeof ( double ) ) ;
2016-01-30 03:00:20 +01:00
* out_buffer_length = sizeof ( double ) ;
return SQL_SUCCESS ;
}
namespace {
// called for each row in the cache when the cache is destroyed in the destructor
2017-06-22 23:04:34 +02:00
void cache_row_dtor ( _In_ zval * data )
2016-01-30 03:00:20 +01:00
{
2016-07-06 20:47:05 +02:00
row_dtor_closure * cl = reinterpret_cast < row_dtor_closure * > ( Z_PTR_P ( data ) ) ;
2016-01-30 03:00:20 +01:00
BYTE * row = cl - > row_data ;
// don't release this here, since this is called from the destructor of the result_set
sqlsrv_buffered_result_set * result_set = cl - > results ;
for ( SQLSMALLINT i = 0 ; i < result_set - > column_count ( ) ; + + i ) {
if ( result_set - > col_meta_data ( i ) . length = = sqlsrv_buffered_result_set : : meta_data : : SIZE_UNKNOWN ) {
2018-08-01 02:22:56 +02:00
void * out_of_row_data = * reinterpret_cast < void * * > ( & row [ result_set - > col_meta_data ( i ) . offset ] ) ;
2016-01-30 03:00:20 +01:00
sqlsrv_free ( out_of_row_data ) ;
}
}
sqlsrv_free ( row ) ;
2016-07-06 20:47:05 +02:00
sqlsrv_free ( cl ) ;
2016-01-30 03:00:20 +01:00
}
2017-06-22 23:04:34 +02:00
SQLPOINTER read_lob_field ( _Inout_ sqlsrv_stmt * stmt , _In_ SQLUSMALLINT field_index , _In_ sqlsrv_buffered_result_set : : meta_data & meta ,
2020-04-21 00:17:21 +02:00
_In_ zend_long mem_used )
2016-01-30 03:00:20 +01:00
{
SQLSMALLINT extra = 0 ;
2016-03-15 04:09:46 +01:00
SQLULEN * output_buffer_len = NULL ;
2016-01-30 03:00:20 +01:00
// Set the amount of space necessary for null characters at the end of the data.
switch ( meta . c_type ) {
case SQL_C_WCHAR :
extra = sizeof ( SQLWCHAR ) ;
break ;
case SQL_C_BINARY :
extra = 0 ;
break ;
case SQL_C_CHAR :
extra = sizeof ( SQLCHAR ) ;
break ;
default :
SQLSRV_ASSERT ( false , " Invalid type in read_lob_field " ) ;
break ;
}
2016-04-12 23:43:46 +02:00
SQLLEN already_read = 0 ;
2017-11-15 20:00:56 +01:00
SQLLEN to_read = INITIAL_LOB_FIELD_LEN ;
2016-01-30 03:00:20 +01:00
sqlsrv_malloc_auto_ptr < char > buffer ;
2017-11-15 20:00:56 +01:00
buffer = static_cast < char * > ( sqlsrv_malloc ( INITIAL_LOB_FIELD_LEN + extra + sizeof ( SQLULEN ) ) ) ;
2016-01-30 03:00:20 +01:00
SQLRETURN r = SQL_SUCCESS ;
2018-08-01 02:22:56 +02:00
SQLCHAR state [ SQL_SQLSTATE_BUFSIZE ] = { ' \0 ' } ;
2016-01-30 03:00:20 +01:00
SQLLEN last_field_len = 0 ;
bool full_length_returned = false ;
do {
2016-03-15 04:09:46 +01:00
output_buffer_len = reinterpret_cast < SQLULEN * > ( buffer . get ( ) ) ;
2016-01-30 03:00:20 +01:00
r = core : : SQLGetData ( stmt , field_index + 1 , meta . c_type , buffer . get ( ) + already_read + sizeof ( SQLULEN ) ,
2020-04-21 00:17:21 +02:00
to_read - already_read + extra , & last_field_len , false /*handle_warning*/ ) ;
2016-01-30 03:00:20 +01:00
// if the field is NULL, then return a NULL pointer
if ( last_field_len = = SQL_NULL_DATA ) {
return NULL ;
}
// if the last read was successful, we're done
if ( r = = SQL_SUCCESS ) {
// check to make sure we haven't overflown our memory limit
CHECK_CUSTOM_ERROR ( mem_used + last_field_len > stmt - > buffered_query_limit * 1024 , stmt ,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED , stmt - > buffered_query_limit ) {
throw core : : CoreException ( ) ;
}
break ;
}
// else if it wasn't the truncated warning (01004) then we're done
else if ( r = = SQL_SUCCESS_WITH_INFO ) {
SQLSMALLINT len ;
core : : SQLGetDiagField ( stmt , 1 , SQL_DIAG_SQLSTATE , state , SQL_SQLSTATE_BUFSIZE , & len
2020-04-21 00:17:21 +02:00
) ;
2016-01-30 03:00:20 +01:00
if ( ! is_truncated_warning ( state ) ) {
break ;
}
}
SQLSRV_ASSERT ( SQL_SUCCEEDED ( r ) , " Unknown SQL error not triggered " ) ;
2018-01-15 20:57:52 +01:00
already_read + = to_read - already_read ;
2016-01-30 03:00:20 +01:00
// if the type of the field returns the total to be read, we use that and preallocate the buffer
if ( last_field_len ! = SQL_NO_TOTAL ) {
CHECK_CUSTOM_ERROR ( mem_used + last_field_len > stmt - > buffered_query_limit * 1024 , stmt ,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED , stmt - > buffered_query_limit ) {
throw core : : CoreException ( ) ;
}
to_read = last_field_len ;
buffer . resize ( to_read + extra + sizeof ( SQLULEN ) ) ;
2016-03-15 04:09:46 +01:00
output_buffer_len = reinterpret_cast < SQLULEN * > ( buffer . get ( ) ) ;
2016-01-30 03:00:20 +01:00
// record the size of the field since we have it available
* output_buffer_len = last_field_len ;
full_length_returned = true ;
}
// otherwise allocate another chunk of memory to read in
else {
to_read * = 2 ;
CHECK_CUSTOM_ERROR ( mem_used + to_read > stmt - > buffered_query_limit * 1024 , stmt ,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED , stmt - > buffered_query_limit ) {
throw core : : CoreException ( ) ;
}
buffer . resize ( to_read + extra + sizeof ( SQLULEN ) ) ;
2016-03-15 04:09:46 +01:00
output_buffer_len = reinterpret_cast < SQLULEN * > ( buffer . get ( ) ) ;
2016-01-30 03:00:20 +01:00
}
} while ( true ) ;
SQLSRV_ASSERT ( output_buffer_len ! = NULL , " Output buffer not allocated properly " ) ;
// most LOB field types return the total length in the last_field_len, but some field types such as XML
// only return the amount read on the last read
if ( ! full_length_returned ) {
* output_buffer_len = already_read + last_field_len ;
}
char * return_buffer = buffer ;
buffer . transferred ( ) ;
return return_buffer ;
}
}