diff --git a/source/pdo_sqlsrv/pdo_stmt.cpp b/source/pdo_sqlsrv/pdo_stmt.cpp index 9ecab26a..7e281afe 100644 --- a/source/pdo_sqlsrv/pdo_stmt.cpp +++ b/source/pdo_sqlsrv/pdo_stmt.cpp @@ -211,15 +211,6 @@ void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z ) } } -// internal helper function to free meta data structures allocated -void meta_data_free( _Inout_ field_meta_data* meta ) -{ - if( meta->field_name ) { - meta->field_name.reset(); - } - sqlsrv_free( meta ); -} - zval convert_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_PHPTYPE sqlsrv_php_type, _Inout_ void** in_val, _In_opt_ SQLLEN field_len ) { zval out_zval; @@ -362,9 +353,6 @@ void stmt_option_fetch_datetime:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_op // PDO SQLSRV statement destructor pdo_sqlsrv_stmt::~pdo_sqlsrv_stmt( void ) { - std::for_each( current_meta_data.begin(), current_meta_data.end(), meta_data_free ); - current_meta_data.clear(); - if( bound_column_param_types ) { sqlsrv_free( bound_column_param_types ); bound_column_param_types = NULL; @@ -1170,8 +1158,7 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt ) core_sqlsrv_next_result( static_cast( stmt->driver_data ) ); // clear the current meta data since the new result will generate new meta data - std::for_each( driver_stmt->current_meta_data.begin(), driver_stmt->current_meta_data.end(), meta_data_free ); - driver_stmt->current_meta_data.clear(); + driver_stmt->clean_up_results_metadata(); // if there are no more result sets, return that it failed. if( driver_stmt->past_next_result_end == true ) { diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index 3a3a8af5..0b4ba0ba 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -1522,6 +1522,10 @@ struct sqlsrv_stmt : public sqlsrv_context { // free sensitivity classification metadata void clean_up_sensitivity_metadata(); + + // free resultset metadata + void clean_up_results_metadata(); + // set query timeout void set_query_timeout(); diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index d87231f8..37cd8622 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -198,6 +198,9 @@ sqlsrv_stmt::~sqlsrv_stmt( void ) // delete sensivity data clean_up_sensitivity_metadata(); + // clean up metadata + clean_up_results_metadata(); + invalidate(); zval_ptr_dtor( ¶m_input_strings ); zval_ptr_dtor( &output_params ); @@ -275,6 +278,24 @@ void sqlsrv_stmt::clean_up_sensitivity_metadata() } } +// internal helper function to free meta data structures allocated +void meta_data_free(_Inout_ field_meta_data* meta) +{ + if (meta->field_name) { + meta->field_name.reset(); + } + sqlsrv_free(meta); +} + +void sqlsrv_stmt::clean_up_results_metadata() +{ + std::for_each(current_meta_data.begin(), current_meta_data.end(), meta_data_free); + current_meta_data.clear(); + + column_count = ACTIVE_NUM_COLS_INVALID; + row_count = ACTIVE_NUM_ROWS_INVALID; +} + void sqlsrv_stmt::set_query_timeout() { if (query_timeout == QUERY_TIMEOUT_INVALID || query_timeout < 0) { @@ -867,6 +888,10 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient "core_sqlsrv_fetch: Invalid value provided for fetch_orientation parameter." ); try { + // first check if the end of all results has been reached + CHECK_CUSTOM_ERROR(stmt->past_next_result_end, stmt, SQLSRV_ERROR_NEXT_RESULT_PAST_END) { + throw core::CoreException(); + } // clear the field cache of the previous fetch zend_hash_clean( Z_ARRVAL( stmt->field_cache )); diff --git a/source/sqlsrv/stmt.cpp b/source/sqlsrv/stmt.cpp index 2aef9dd6..3a268fd8 100644 --- a/source/sqlsrv/stmt.cpp +++ b/source/sqlsrv/stmt.cpp @@ -106,15 +106,6 @@ bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_p } -// internal helper function to free meta data structures allocated -void meta_data_free( _Inout_ field_meta_data* meta ) -{ - if( meta->field_name ) { - meta->field_name.reset(); - } - sqlsrv_free( meta ); -} - // query options for cursor types namespace SSCursorTypes { @@ -144,9 +135,6 @@ ss_sqlsrv_stmt::ss_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ ss_sqlsrv_stmt::~ss_sqlsrv_stmt( void ) { - std::for_each(current_meta_data.begin(), current_meta_data.end(), meta_data_free); - current_meta_data.clear(); - if( fetch_field_names != NULL ) { for( int i=0; i < fetch_fields_count; ++i ) { @@ -578,8 +566,7 @@ PHP_FUNCTION( sqlsrv_next_result ) core_sqlsrv_next_result( stmt, true ); // clear the current meta data since the new result will generate new meta data - std::for_each(stmt->current_meta_data.begin(), stmt->current_meta_data.end(), meta_data_free); - stmt->current_meta_data.clear(); + stmt->clean_up_results_metadata(); if( stmt->past_next_result_end ) { diff --git a/test/functional/sqlsrv/sqlsrv_empty_result_error.phpt b/test/functional/sqlsrv/sqlsrv_empty_result_error.phpt index 92b7f0ae..e751df8b 100644 --- a/test/functional/sqlsrv/sqlsrv_empty_result_error.phpt +++ b/test/functional/sqlsrv/sqlsrv_empty_result_error.phpt @@ -11,21 +11,8 @@ require_once("MsCommon.inc"); // These are the error messages we expect at various points below $errorNoMoreResults = "There are no more results returned by the query."; -$errorNoMoreRows = "There are no more rows in the active result set. Since this result set is not scrollable, no more data may be retrieved."; $errorNoFields = "The active result for the query contains no fields."; -// Variable function gets an error message that depends on the OS -function getFuncSeqError() -{ - if ( strtoupper( substr( php_uname( 's' ),0,3 ) ) === 'WIN' ) { - return "[Microsoft][ODBC Driver Manager] Function sequence error"; - } else { - return "[unixODBC][Driver Manager]Function sequence error"; - } -} - -$errorFuncSeq = 'getFuncSeqError'; - // This function takes an array of expected error messages and compares the // contents to the actual errors function CheckError($expectedErrors) @@ -99,16 +86,16 @@ echo "Nonempty result set, call fetch first: ###############################\n"; $stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='b'"); Fetch($stmt, []); NextResult($stmt, []); -Fetch($stmt, [$errorFuncSeq()]); -NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]); +Fetch($stmt, [$errorNoMoreResults]); +NextResult($stmt, [$errorNoMoreResults]); // Call next_result on a nonempty result set echo "Nonempty result set, call next_result first: #########################\n"; $stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='b'"); NextResult($stmt, []); -Fetch($stmt, [$errorFuncSeq()]); -NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]); +Fetch($stmt, [$errorNoMoreResults]); +NextResult($stmt, [$errorNoMoreResults]); // Call next_result twice in succession on a nonempty result set echo "Nonempty result set, call next_result twice: #########################\n"; @@ -123,7 +110,7 @@ echo "Empty result set, call fetch first: ##################################\n"; $stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='w'"); Fetch($stmt, []); NextResult($stmt, []); -Fetch($stmt, [$errorNoMoreRows]); +Fetch($stmt, [$errorNoMoreResults]); NextResult($stmt, [$errorNoMoreResults]); // Call next_result on an empty result set @@ -131,8 +118,8 @@ echo "Empty result set, call next_result first: ############################\n"; $stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='w'"); NextResult($stmt, []); -Fetch($stmt, [$errorFuncSeq()]); -NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]); +Fetch($stmt, [$errorNoMoreResults]); +NextResult($stmt, [$errorNoMoreResults]); // Call next_result twice in succession on an empty result set echo "Empty result set, call next_result twice: ############################\n"; @@ -153,7 +140,7 @@ echo "Null result set, call next result first: #############################\n"; $stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'"); NextResult($stmt, []); -Fetch($stmt, [$errorNoFields]); +Fetch($stmt, [$errorNoMoreResults]); // Call next_result twice in succession on a null result set echo "Null result set, call next result twice: #############################\n";