in source/pdo_sqlsrv/pdo_stmt.cpp [520:629]
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
PDO_LOG_STMT_ENTRY;
try {
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_execute: driver_data object was null" );
// prepare for execution by flushing anything remaining in the result set if it wasn't already
// done before binding parameters
if( driver_stmt && driver_stmt->executed && !driver_stmt->past_next_result_end ) {
while( driver_stmt->past_next_result_end == false ) {
core_sqlsrv_next_result( driver_stmt, false );
}
}
const char* query = NULL;
unsigned int query_len = 0;
// if the user is doing a direct query (PDO::SQLSRV_ATTR_DIRECT_QUERY), set the query here
if( driver_stmt->direct_query ) {
query = driver_stmt->direct_query_subst_string;
query_len = static_cast<unsigned int>( driver_stmt->direct_query_subst_string_len );
}
// if the user is using prepare emulation (PDO::ATTR_EMULATE_PREPARES), set the query to the
// subtituted query provided by PDO
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
// reset the placeholders hashtable internal in case the user reexecutes a statement
// Normally it's not a good idea to alter the internal pointer in a hashed array
// (see pull request 634 on GitHub) but in this case this is for internal use only
zend_hash_internal_pointer_reset(driver_stmt->placeholders);
#if PHP_VERSION_ID < 80100
query = stmt->active_query_string;
query_len = static_cast<unsigned int>(stmt->active_query_stringlen);
#else
query = ZSTR_VAL(stmt->active_query_string);
query_len = ZSTR_LEN(stmt->active_query_string);
#endif
}
// The query timeout setting is inherited from the corresponding connection attribute, but
// the user may have changed the query timeout setting again before this via
// PDOStatement::setAttribute()
driver_stmt->set_query_timeout();
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt, query, query_len );
if ( execReturn == SQL_NO_DATA ) {
stmt->column_count = 0;
stmt->row_count = 0;
driver_stmt->column_count = 0;
driver_stmt->row_count = 0;
}
else {
if (driver_stmt->column_count == ACTIVE_NUM_COLS_INVALID) {
stmt->column_count = core::SQLNumResultCols( driver_stmt );
driver_stmt->column_count = stmt->column_count;
}
else {
stmt->column_count = driver_stmt->column_count;
}
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 );
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
// the following sequence:
// 1) Prepare and execute a statement (that has some results with it)
// 2) call PDOStatement::nextRowset until there are no more results
// 3) execute the statement again
// 4) call PDOStatement::getColumnMeta
// It crashes from what I can tell because there is no metadata because there was no call to
// pdo_stmt_sqlsrv_describe_col and stmt->columns is NULL on the second call to
// PDO::execute. My guess is that because stmt->executed is true, it optimizes away a necessary call to
// pdo_sqlsrv_stmt_describe_col. By setting the stmt->executed flag to 0, this call is not optimized away
// and the crash disappears.
if( stmt->columns == NULL ) {
stmt->executed = 0;
}
}
catch( core::CoreException& /*e*/ ) {
return 0;
}
catch( ... ) {
DIE( "pdo_sqlsrv_stmt_execute: Unexpected exception occurred." );
}
// success
return 1;
}