void core_get_field_common()

in source/shared/core_stmt.cpp [1368:1540]


void core_get_field_common( _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 )
{
    try {

        close_active_stream( stmt );

        // make sure that fetch is called before trying to retrieve.
        CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) {
            throw core::CoreException();
        }

        // make sure that fields are not retrieved incorrectly.
        CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index,
                            stmt->last_field_index ) {
            throw core::CoreException();
        }

        switch( sqlsrv_php_type.typeinfo.type ) {

        case SQLSRV_PHPTYPE_INT:
        {
            sqlsrv_malloc_auto_ptr<SQLLEN> field_value_temp;
            field_value_temp = static_cast<SQLLEN*>( sqlsrv_malloc( sizeof( SQLLEN )));
            *field_value_temp = 0;

            SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( SQLLEN ),
                                                           field_len, true /*handle_warning*/ );

            CHECK_SQL_ERROR_OR_WARNING( 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 == SQL_NULL_DATA ) {
                field_value = NULL;
                break;
            }

            field_value = field_value_temp;
            field_value_temp.transferred();
            break;
        }

        case SQLSRV_PHPTYPE_FLOAT:
        {
            sqlsrv_malloc_auto_ptr<double> field_value_temp;
            field_value_temp = static_cast<double*>( sqlsrv_malloc( sizeof( double )));
            *field_value_temp = 0.0;

            SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ),
                                                           field_len, true /*handle_warning*/ );

            CHECK_SQL_ERROR_OR_WARNING( 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 == SQL_NULL_DATA ) {
                field_value = NULL;
                break;
            }

            field_value = field_value_temp;
            field_value_temp.transferred();
            break;
        }

        case SQLSRV_PHPTYPE_STRING:
        {
            get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len );
            break;
        }

        // Reference: https://docs.microsoft.com/sql/odbc/reference/appendixes/sql-to-c-timestamp
        // Retrieve the datetime data as a string, which may be cached for later use.
        // The string is converted to a DateTime object only when it is required to
        // be returned as a zval.
        case SQLSRV_PHPTYPE_DATETIME:
        {
            sqlsrv_malloc_auto_ptr<char> field_value_temp;
            SQLLEN field_len_temp = 0;

            field_value_temp = static_cast<char*>(sqlsrv_malloc(MAX_DATETIME_STRING_LEN));
            memset(field_value_temp, '\0', MAX_DATETIME_STRING_LEN);

            SQLRETURN r = stmt->current_results->get_data(field_index + 1, SQL_C_CHAR, field_value_temp, MAX_DATETIME_STRING_LEN, &field_len_temp, true);

            if (r == SQL_NO_DATA || field_len_temp == SQL_NULL_DATA) {
                field_value_temp.reset();
                field_len_temp = 0;
            }

            CHECK_CUSTOM_ERROR((r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index) {
                throw core::CoreException();
            }

            field_value = field_value_temp;
            field_value_temp.transferred();
            *field_len = field_len_temp;

            break;
        }

        // create a stream wrapper around the field and return that object to the PHP script.  calls to fread
        // on the stream will result in calls to SQLGetData.  This is handled in stream.cpp.  See that file
        // for how these fields are used.
        case SQLSRV_PHPTYPE_STREAM:
        {
            php_stream* stream = NULL;
            sqlsrv_stream* ss = NULL;
            SQLSMALLINT sql_type;

            SQLSRV_ASSERT(stmt->current_meta_data.size() > field_index, "core_get_field_common - meta data vector not in sync" );
            sql_type = stmt->current_meta_data[field_index]->field_type;

            CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) {
                throw core::CoreException();
            }

            // For a sqlsrv stream, only REPORT_ERRORS may be used. For "mode", the 'b' option 
            // is ignored on POSIX systems, which treat text and binary files the same. Yet, the
            // 'b' option might be important in other systems.
            // For details check https://www.php.net/manual/en/internals2.ze1.streams.php
            stream = php_stream_open_wrapper("sqlsrv://sqlncli10", "rb", REPORT_ERRORS, NULL);

            CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) {
                throw core::CoreException();
            }

            ss = static_cast<sqlsrv_stream*>( stream->abstract );
            ss->stmt = stmt;
            ss->field_index = field_index;
            ss->sql_type = static_cast<SQLUSMALLINT>( sql_type );
            ss->encoding = static_cast<SQLSRV_ENCODING>( sqlsrv_php_type.typeinfo.encoding );

            zval_auto_ptr return_value_z;
            return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval ));
            ZVAL_UNDEF( return_value_z );

            // turn our stream into a zval to be returned
            php_stream_to_zval( stream, return_value_z );

            field_value = reinterpret_cast<void*>( return_value_z.get());
            return_value_z.transferred();
            break;
        }

        case SQLSRV_PHPTYPE_NULL:
            field_value = NULL;
            *field_len = 0;
            break;

        default:
            DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" );
            break;
        }

        // sucessfully retrieved the field, so update our last retrieved field
        if( stmt->last_field_index < field_index ) {
            stmt->last_field_index = field_index;
        }
    }
    catch( core::CoreException& e ) {
        throw e;
    }
}