SQLPOINTER read_lob_field()

in source/shared/core_results.cpp [1472:1579]


SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ sqlsrv_buffered_result_set::meta_data& meta, 
                           _In_ zend_long mem_used )
{
    SQLSMALLINT extra = 0;
    SQLULEN* output_buffer_len = NULL;

    // 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;
    }

    SQLLEN already_read = 0;
    SQLLEN to_read = INITIAL_LOB_FIELD_LEN;
    sqlsrv_malloc_auto_ptr<char> buffer;
    buffer = static_cast<char*>( sqlsrv_malloc( INITIAL_LOB_FIELD_LEN + extra + sizeof( SQLULEN )));
    SQLRETURN r = SQL_SUCCESS;
    SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = {'\0'};
    SQLLEN last_field_len = 0;
    bool full_length_returned = false;

    do {


        output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
        r = core::SQLGetData( stmt, field_index + 1, meta.c_type, buffer.get() + already_read + sizeof( SQLULEN ),
                              to_read - already_read + extra, &last_field_len, false /*handle_warning*/ );

        // 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 
                                   );

            if( !is_truncated_warning( state )) {
                break;
            }
        }

        SQLSRV_ASSERT( SQL_SUCCEEDED( r ), "Unknown SQL error not triggered" );

        already_read += to_read - already_read;
        // 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 ));
            output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
            // 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 ));
            output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
        }

    } 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;
}