From 67af1dd2f4ca28f420a1e96730d5f8776105b77e Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 21 Sep 2026 22:09:26 -0400 Subject: [PATCH] ext/pdo: Throw a ValueError from bindColumn() for an unknown column A column name that is not in the result set is a programming error, so report it the way the method already reports an empty name or an index below one, rather than through PDO::ATTR_ERRMODE. The equivalent parameter failure in bindParam() and bindValue() stays an ERRMODE error: its site in rewrite_name_to_position() is only reachable once execute() has populated bound_param_map, never from the bind methods themselves. Closes GH-23835 --- UPGRADING | 5 ++++ ext/pdo/pdo_stmt.c | 7 +---- .../tests/pdo_bindcolumn_unknown_column.phpt | 30 +++++++++++-------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/UPGRADING b/UPGRADING index 7f1fc588bd03..13e5ebf870ba 100644 --- a/UPGRADING +++ b/UPGRADING @@ -120,6 +120,11 @@ PHP 8.6 UPGRADE NOTES execution error occurs (e.g. malformed UTF-8 input with the /u modifier). This is consistent with other preg_* functions. +- PDO: + . PDOStatement::bindColumn() now throws a ValueError when the column name is + not present in the result set. It previously reported the condition + through PDO::ATTR_ERRMODE and returned false. + - PGSQL: . pg_fetch_object() now reports the ValueError for a non-empty $constructor_args on a class without a constructor on the diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 0eb6645608e7..9306a0999199 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -299,12 +299,7 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_ /* if you prepare and then execute passing an array of params keyed by names, * then this will trigger, and we don't want that */ if (param->paramno == -1) { - /* Should this always be an Error? */ - char *tmp; - /* TODO Error? */ - spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", ZSTR_VAL(param->name)); - pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp); - efree(tmp); + zend_argument_value_error(1, "must refer to a column present in the result set, \"%s\" given", ZSTR_VAL(param->name)); return false; } } diff --git a/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt b/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt index 44e85ec07c31..3e3ba30e8c41 100644 --- a/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt +++ b/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO: bindColumn() must fail for a column name that is not in the result set +PDO: bindColumn() must throw for a column name that is not in the result set --EXTENSIONS-- pdo --SKIPIF-- @@ -17,16 +17,20 @@ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; $db = PDOTest::factory(); $db->exec('CREATE TABLE pdo_bindcolumn_unknown_column (name varchar(255))'); -$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); -$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column'); -var_dump(@$stmt->bindColumn('nosuchcolumn', $var)); - -$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -try { - $stmt->bindColumn('nosuchcolumn', $var); -} catch (PDOException $e) { - echo $e::class, ": ", $e->getMessage(), PHP_EOL; +// The error mode must not affect a ValueError. +foreach ([PDO::ERRMODE_SILENT, PDO::ERRMODE_WARNING, PDO::ERRMODE_EXCEPTION] as $mode) { + $db->setAttribute(PDO::ATTR_ERRMODE, $mode); + $stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column'); + try { + $stmt->bindColumn('nosuchcolumn', $var); + } catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } } + +// A column that does exist still binds. +$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column'); +var_dump($stmt->bindColumn('name', $var)); ?> --CLEAN-- exec('DROP TABLE pdo_bindcolumn_unknown_column'); ?> --EXPECT-- -bool(false) -PDOException: SQLSTATE[HY000]: General error: Did not find column name 'nosuchcolumn' in the defined columns; it will not be bound +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given +bool(true)