Skip to content

Commit d5696b8

Browse files
committed
Fix GH-24372: Keep stmt->executed set across PDOStatement::execute()
11690db cleared stmt->executed on entry to execute() so a failed execute() could not leave the previous result fetchable. Drivers read that flag as "executed successfully at least once": pdo_oci reallocates its column buffers when it is clear, while PDO core skips describing the columns again because they are still set, so the OCI defines keep writing into the freed buffers; pdo_ibm leaks its column descriptors. Record the failed execute() in a separate bit instead. The cursor closer still runs on entry, but drivers without one are no longer drained, which also made them fetch every remaining row on each re-execute. Fixes GH-24372 Closes GH-24379
1 parent 2998188 commit d5696b8

2 files changed

Lines changed: 7 additions & 27 deletions

File tree

‎ext/pdo/pdo_stmt.c‎

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -391,29 +391,6 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_
391391
}
392392
/* }}} */
393393

394-
static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt);
395-
396-
static void pdo_stmt_invalidate_result(pdo_stmt_t *stmt)
397-
{
398-
if (stmt->methods->cursor_closer) {
399-
stmt->methods->cursor_closer(stmt);
400-
} else {
401-
do {
402-
while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
403-
;
404-
if (!stmt->methods->next_rowset) {
405-
break;
406-
}
407-
408-
if (!pdo_stmt_do_next_rowset(stmt)) {
409-
break;
410-
}
411-
} while (1);
412-
}
413-
414-
stmt->executed = 0;
415-
}
416-
417394
/* {{{ Execute a prepared statement, optionally binding parameters */
418395
PHP_METHOD(PDOStatement, execute)
419396
{
@@ -430,9 +407,10 @@ PHP_METHOD(PDOStatement, execute)
430407
RETURN_THROWS();
431408
}
432409

433-
if (stmt->executed) {
434-
pdo_stmt_invalidate_result(stmt);
410+
if (stmt->executed && stmt->methods->cursor_closer) {
411+
stmt->methods->cursor_closer(stmt);
435412
}
413+
stmt->execute_incomplete = 1;
436414
PDO_STMT_CLEAR_ERR();
437415

438416
if (input_params) {
@@ -499,6 +477,7 @@ PHP_METHOD(PDOStatement, execute)
499477
RETURN_FALSE;
500478
}
501479
if (stmt->methods->executer(stmt)) {
480+
stmt->execute_incomplete = 0;
502481
if (!stmt->executed) {
503482
/* this is the first execute */
504483

@@ -605,7 +584,7 @@ static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno, enum pdo
605584

606585
static bool do_fetch_common(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
607586
{
608-
if (!stmt->executed) {
587+
if (!stmt->executed || stmt->execute_incomplete) {
609588
return 0;
610589
}
611590

‎ext/pdo/php_pdo_driver.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,9 @@ struct _pdo_stmt_t {
568568
* emulate prepare and bind on its behalf */
569569
unsigned supports_placeholders:2;
570570
unsigned in_param_event:1;
571+
unsigned execute_incomplete:1;
571572

572-
unsigned _reserved:28;
573+
unsigned _reserved:27;
573574

574575
/* the number of columns in the result set; not valid until after
575576
* the statement has been executed at least once. In some cases, might

0 commit comments

Comments
 (0)