diff --git a/source/pdo_sqlsrv/pdo_dbh.cpp b/source/pdo_sqlsrv/pdo_dbh.cpp index a5ad855f..97c26b33 100644 --- a/source/pdo_sqlsrv/pdo_dbh.cpp +++ b/source/pdo_sqlsrv/pdo_dbh.cpp @@ -690,21 +690,22 @@ zend_long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, size_t sql_len TSR NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); driver_stmt->set_func( __FUNCTION__ ); - core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast( sql_len ) ); + SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast( sql_len ) ); // since the user can give us a compound statement, we return the row count for the last set, and since the row count // isn't guaranteed to be valid until all the results have been fetched, we fetch them all first. - if( core_sqlsrv_has_any_result( driver_stmt TSRMLS_CC )) { + + if ( execReturn != SQL_NO_DATA && core_sqlsrv_has_any_result( driver_stmt TSRMLS_CC )) { SQLRETURN r = SQL_SUCCESS; do { - + rows = core::SQLRowCount( driver_stmt TSRMLS_CC ); r = core::SQLMoreResults( driver_stmt TSRMLS_CC ); - - } while( r != SQL_NO_DATA ); + + } while ( r != SQL_NO_DATA ); } // returning -1 forces PDO to return false, which signals an error occurred. SQLRowCount returns -1 for a number of cases diff --git a/source/pdo_sqlsrv/pdo_stmt.cpp b/source/pdo_sqlsrv/pdo_stmt.cpp index 90990e4c..b2dc45ea 100644 --- a/source/pdo_sqlsrv/pdo_stmt.cpp +++ b/source/pdo_sqlsrv/pdo_stmt.cpp @@ -552,12 +552,18 @@ int pdo_sqlsrv_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) query_len = static_cast( stmt->active_query_stringlen ); } - core_sqlsrv_execute( driver_stmt TSRMLS_CC, query, query_len ); + SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, query, query_len ); - stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC ); + if ( execReturn == SQL_NO_DATA ) { + stmt->column_count = 0; + stmt->row_count = 0; + } + else { + stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC ); - // return the row count regardless if there are any rows or not - stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC ); + // return the row count regardless if there are any rows or not + stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC ); + } // workaround for a bug in the PDO driver manager. It is fairly simple to crash the PDO driver manager with // the following sequence: diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index 4adb72e8..cff68fb1 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -1364,7 +1364,7 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLINT direction, zval* param_z, SQLSRV_PHPTYPE php_out_type, SQLSRV_ENCODING encoding, SQLSMALLINT sql_type, SQLULEN column_size, SQLSMALLINT decimal_digits TSRMLS_DC ); -void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql = NULL, int sql_len = 0 ); +SQLRETURN core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql = NULL, int sql_len = 0 ); field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT colno TSRMLS_DC ); bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC ); void core_sqlsrv_get_field(sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_phptype, bool prefer_string, @@ -2041,7 +2041,7 @@ namespace core { SQLRETURN r; SQLSMALLINT num_cols; r = ::SQLNumResultCols( stmt->handle(), &num_cols ); - + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { throw CoreException(); } diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 2c73f5fd..1a1fe359 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -676,9 +676,9 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALL // Return: // true if there is data, false if there is not -void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_len ) +SQLRETURN core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_len ) { - SQLRETURN r; + SQLRETURN r = SQL_ERROR; try { @@ -709,7 +709,7 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_ r = core::SQLExecute( stmt TSRMLS_CC ); } - // if data is needed (streams were bound) and they should be sent at execute time, then do so now + // if data is needed (streams were bound) and they should be sent at execute time, then do so now if( r == SQL_NEED_DATA && stmt->send_streams_at_exec ) { send_param_streams( stmt TSRMLS_CC ); @@ -723,20 +723,21 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_ finalize_output_parameters( stmt TSRMLS_CC ); } - // stream parameters are sent, clean the Hashtable - if ( stmt->send_streams_at_exec ) { - zend_hash_clean( Z_ARRVAL( stmt->param_streams )); - } + // stream parameters are sent, clean the Hashtable + if ( stmt->send_streams_at_exec ) { + zend_hash_clean( Z_ARRVAL( stmt->param_streams )); + } + return r; } catch( core::CoreException& e ) { // if the statement executed but failed in a subsequent operation before returning, // we need to cancel the statement and deref the output and stream parameters - if ( stmt->send_streams_at_exec ) { - zend_hash_clean( Z_ARRVAL( stmt->output_params )); - zend_hash_clean( Z_ARRVAL( stmt->param_streams )); - } - if( stmt->executed ) { + if ( stmt->send_streams_at_exec ) { + zend_hash_clean( Z_ARRVAL( stmt->output_params )); + zend_hash_clean( Z_ARRVAL( stmt->param_streams )); + } + if( stmt->executed ) { SQLCancel( stmt->handle() ); // stmt->executed = false; should this be reset if something fails? } diff --git a/test/pdo_sqlsrv/pdo_336_pho_exec_empty_result_set_error.phpt b/test/pdo_sqlsrv/pdo_336_pho_exec_empty_result_set_error.phpt new file mode 100644 index 00000000..9a6bb61f --- /dev/null +++ b/test/pdo_sqlsrv/pdo_336_pho_exec_empty_result_set_error.phpt @@ -0,0 +1,60 @@ +--TEST-- +GitHub issue #336 - PDO::exec should not return an error with query returning SQL_NO_DATA +--DESCRIPTION-- +Verifies GitHub issue 336 is fixed, PDO::exec on query returning SQL_NO_DATA will not give an error +--SKIPIF-- +--FILE-- +setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + +$sql = "DELETE FROM foo_table WHERE id = 42"; +$sqlWithParameter = "DELETE FROM foo_table WHERE id = :id"; +$sqlParameter = 42; + +$Statement = $conn->exec("IF OBJECT_ID('foo_table', 'U') IS NOT NULL DROP TABLE foo_table"); +$Statement = $conn->exec("CREATE TABLE foo_table (id BIGINT PRIMARY KEY NOT NULL IDENTITY, intField INT NOT NULL)"); +$Statement = $conn->exec("INSERT INTO foo_table (intField) VALUES(3)"); + +//test prepare, not args +$stmt = $conn->prepare($sql); +$stmt->execute(); +if ($conn->errorCode() == "00000") + echo "prepare OK\n"; +else + echo "unexpected error at prepare"; + +//test prepare, with args +$stmt = $conn->prepare($sqlWithParameter); +$stmt->execute(array(':id' => $sqlParameter)); +if ($conn->errorCode() == "00000") + echo "prepare with args OK\n"; +else + echo "unexpected error at prepare with args"; + +//test direct exec +$stmt = $conn->exec($sql); +$err = $conn->errorCode(); +if ($stmt == 0 && $err == "00000") + echo "direct exec OK\n"; +else + if ($stmt != 0) + echo "unexpected row returned at direct exec\n"; + if ($err != "00000") + echo "unexpected error at direct exec"; + +$Statement = $conn->exec("IF OBJECT_ID('foo_table', 'U') IS NOT NULL DROP TABLE foo_table"); + +$stmt = NULL; +$Statement = NULL; +$conn = NULL; + +?> +--EXPECT-- +prepare OK +prepare with args OK +direct exec OK