Skip to content

Commit

Permalink
Merge pull request #972 from david-puglielli/redundant-apis
Browse files Browse the repository at this point in the history
Fix for redundant calls to SQLNumResultCols and SQLRowCount
  • Loading branch information
david-puglielli authored Apr 15, 2019
2 parents fd24a97 + ad1d990 commit a3456cd
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 17 deletions.
34 changes: 28 additions & 6 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,12 +593,26 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
if ( execReturn == SQL_NO_DATA ) {
stmt->column_count = 0;
stmt->row_count = 0;
driver_stmt->column_count = 0;
driver_stmt->row_count = 0;
}
else {
stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC );
if (driver_stmt->column_count == ACTIVE_NUM_COLS_INVALID) {
stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC );
driver_stmt->column_count = stmt->column_count;
}
else {
stmt->column_count = driver_stmt->column_count;
}

// return the row count regardless if there are any rows or not
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
if (driver_stmt->row_count == ACTIVE_NUM_ROWS_INVALID) {
// return the row count regardless if there are any rows or not
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
driver_stmt->row_count = stmt->row_count;
}
else {
stmt->row_count = driver_stmt->row_count;
}
}

// workaround for a bug in the PDO driver manager. It is fairly simple to crash the PDO driver manager with
Expand Down Expand Up @@ -692,11 +706,16 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
SQLSMALLINT odbc_fetch_ori = pdo_fetch_ori_to_odbc_fetch_ori( ori );
bool data = core_sqlsrv_fetch( driver_stmt, odbc_fetch_ori, offset TSRMLS_CC );

// support for the PDO rowCount method. Since rowCount doesn't call a method, PDO relies on us to fill the
// pdo_stmt_t::row_count member
if( driver_stmt->past_fetch_end || driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY ) {
// support for the PDO rowCount method. Since rowCount doesn't call a
// method, PDO relies on us to fill the pdo_stmt_t::row_count member
// The if condition was changed from
// `driver_stmt->past_fetch_end || driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY`
// because it caused SQLRowCount to be called at each fetch if using a non-forward cursor
// which is unnecessary and a performance hit
if( driver_stmt->past_fetch_end || driver_stmt->cursor_type == SQL_CURSOR_DYNAMIC) {

stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
driver_stmt->row_count = stmt->row_count;

// a row_count of -1 means no rows, but we change it to 0
if( stmt->row_count == -1 ) {
Expand Down Expand Up @@ -1146,6 +1165,9 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )

// return the row count regardless if there are any rows or not
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );

driver_stmt->column_count = stmt->column_count;
driver_stmt->row_count = stmt->row_count;
}
catch( core::CoreException& ) {

Expand Down
10 changes: 8 additions & 2 deletions source/shared/core_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ const int SQL_MAX_ERROR_MESSAGE_LENGTH = SQL_MAX_MESSAGE_LENGTH * 2;
// max size of a date time string when converting from a DateTime object to a string
const int MAX_DATETIME_STRING_LEN = 256;

// identifier for whether or not we have obtained the number of rows and columns
// of a result
const short ACTIVE_NUM_COLS_INVALID = -99;
const long ACTIVE_NUM_ROWS_INVALID = -99;

// precision and scale for the date time types between servers
const int SQL_SERVER_2005_DEFAULT_DATETIME_PRECISION = 23;
const int SQL_SERVER_2005_DEFAULT_DATETIME_SCALE = 3;
Expand Down Expand Up @@ -1438,8 +1443,9 @@ struct sqlsrv_stmt : public sqlsrv_context {
bool has_rows; // Has_rows is set if there are actual rows in the row set
bool fetch_called; // Used by core_sqlsrv_get_field to return an informative error if fetch not yet called
int last_field_index; // last field retrieved by core_sqlsrv_get_field
bool past_next_result_end; // core_sqlsrv_next_result sets this to true when the statement goes beyond the
// last results
bool past_next_result_end; // core_sqlsrv_next_result sets this to true when the statement goes beyond the last results
short column_count; // Number of columns in the current result set obtained from SQLNumResultCols
long row_count; // Number of rows in the current result set obtained from SQLRowCount
unsigned long query_timeout; // maximum allowed statement execution time
zend_long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB)
bool date_as_string; // false by default but the user can set this to true to retrieve datetime values as strings
Expand Down
39 changes: 34 additions & 5 deletions source/shared/core_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error
fetch_called( false ),
last_field_index( -1 ),
past_next_result_end( false ),
column_count( ACTIVE_NUM_COLS_INVALID ),
row_count( ACTIVE_NUM_ROWS_INVALID ),
query_timeout( QUERY_TIMEOUT_INVALID ),
date_as_string(false),
format_decimals(false), // no formatting needed
Expand Down Expand Up @@ -225,6 +227,8 @@ void sqlsrv_stmt::new_result_set( TSRMLS_D )
this->past_next_result_end = false;
this->past_fetch_end = false;
this->last_field_index = -1;
this->column_count = ACTIVE_NUM_COLS_INVALID;
this->row_count = ACTIVE_NUM_ROWS_INVALID;

// delete any current results
if( current_results ) {
Expand Down Expand Up @@ -819,9 +823,17 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient
CHECK_CUSTOM_ERROR( stmt->past_fetch_end, stmt, SQLSRV_ERROR_FETCH_PAST_END ) {
throw core::CoreException();
}

// First time only
if ( !stmt->fetch_called ) {
SQLSMALLINT has_fields = core::SQLNumResultCols( stmt TSRMLS_CC );
SQLSMALLINT has_fields;
if (stmt->column_count != ACTIVE_NUM_COLS_INVALID) {
has_fields = stmt->column_count;
} else {
has_fields = core::SQLNumResultCols( stmt TSRMLS_CC );
stmt->column_count = has_fields;
}

CHECK_CUSTOM_ERROR( has_fields == 0, stmt, SQLSRV_ERROR_NO_FIELDS ) {
throw core::CoreException();
}
Expand Down Expand Up @@ -1066,10 +1078,27 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i

bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
{
// Use SQLNumResultCols to determine if we have rows or not.
SQLSMALLINT num_cols = core::SQLNumResultCols( stmt TSRMLS_CC );
// use SQLRowCount to determine if there is a rows status waiting
SQLLEN rows_affected = core::SQLRowCount( stmt TSRMLS_CC );
SQLSMALLINT num_cols;
SQLLEN rows_affected;

if (stmt->column_count != ACTIVE_NUM_COLS_INVALID) {
num_cols = stmt->column_count;
}
else {
// Use SQLNumResultCols to determine if we have rows or not
num_cols = core::SQLNumResultCols( stmt TSRMLS_CC );
stmt->column_count = num_cols;
}

if (stmt->row_count != ACTIVE_NUM_ROWS_INVALID) {
rows_affected = stmt->row_count;
}
else {
// Use SQLRowCount to determine if there is a rows status waiting
rows_affected = core::SQLRowCount( stmt TSRMLS_CC );
stmt->row_count = rows_affected;
}

return (num_cols != 0) || (rows_affected > 0);
}

Expand Down
7 changes: 6 additions & 1 deletion source/sqlsrv/stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1791,7 +1791,12 @@ SQLSMALLINT get_resultset_meta_data(_Inout_ sqlsrv_stmt * stmt)

if (num_cols == 0) {
getMetaData = true;
num_cols = core::SQLNumResultCols(stmt TSRMLS_CC);
if (stmt->column_count == ACTIVE_NUM_COLS_INVALID) {
num_cols = core::SQLNumResultCols(stmt TSRMLS_CC);
stmt->column_count = num_cols;
} else {
num_cols = stmt->column_count;
}
}

try {
Expand Down
4 changes: 2 additions & 2 deletions source/sqlsrv/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ ss_error SS_ERRORS[] = {
},

{
SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED,
{ IMSSP, (SQLCHAR*)"Failed to create an instance of class %1!s!.", -30, true }
SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED,
{ IMSSP, (SQLCHAR*)"Failed to create an instance of class %1!s!.", -30, true }
},

{
Expand Down
2 changes: 1 addition & 1 deletion test/functional/sqlsrv/sqlsrv_empty_result_error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ echo "Null result set, call next result first: #############################\n";

$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'");
NextResult($stmt, []);
Fetch($stmt, [$errorFuncSeq()]);
Fetch($stmt, [$errorNoFields]);

// Call next_result twice in succession on a null result set
echo "Null result set, call next result twice: #############################\n";
Expand Down

0 comments on commit a3456cd

Please sign in to comment.