Skip to content

Commit 65b257d

Browse files
committed
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
1 parent f49cdd4 commit 65b257d

4 files changed

Lines changed: 31 additions & 22 deletions

File tree

‎UPGRADING‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ PHP 8.7 UPGRADE NOTES
1919
1. Backward Incompatible Changes
2020
========================================
2121

22+
- PDO:
23+
. PDOStatement::bindColumn() now throws a ValueError when the column name is
24+
not present in the result set. It previously reported the condition
25+
through PDO::ATTR_ERRMODE and returned false.
26+
2227
- Standard:
2328
. The number of filters that can be chained in a php://filter URL is limited
2429
to 16 by default. Set the stream context option max_filter_count to change

‎ext/pdo/pdo_stmt.c‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,7 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_
295295
/* if you prepare and then execute passing an array of params keyed by names,
296296
* then this will trigger, and we don't want that */
297297
if (param->paramno == -1) {
298-
/* Should this always be an Error? */
299-
char *tmp;
300-
/* TODO Error? */
301-
spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", ZSTR_VAL(param->name));
302-
pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp);
303-
efree(tmp);
298+
zend_argument_value_error(1, "must refer to a column present in the result set, \"%s\" given", ZSTR_VAL(param->name));
304299
return false;
305300
}
306301
}

‎ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt‎

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
PDO: bindColumn() must fail for a column name that is not in the result set
2+
PDO: bindColumn() must throw for a column name that is not in the result set
33
--EXTENSIONS--
44
pdo
55
--SKIPIF--
@@ -17,16 +17,20 @@ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
1717
$db = PDOTest::factory();
1818
$db->exec('CREATE TABLE pdo_bindcolumn_unknown_column (name varchar(255))');
1919

20-
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
21-
$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column');
22-
var_dump(@$stmt->bindColumn('nosuchcolumn', $var));
23-
24-
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
25-
try {
26-
$stmt->bindColumn('nosuchcolumn', $var);
27-
} catch (PDOException $e) {
28-
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
20+
// The error mode must not affect a ValueError.
21+
foreach ([PDO::ERRMODE_SILENT, PDO::ERRMODE_WARNING, PDO::ERRMODE_EXCEPTION] as $mode) {
22+
$db->setAttribute(PDO::ATTR_ERRMODE, $mode);
23+
$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column');
24+
try {
25+
$stmt->bindColumn('nosuchcolumn', $var);
26+
} catch (ValueError $e) {
27+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
28+
}
2929
}
30+
31+
// A column that does exist still binds.
32+
$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column');
33+
var_dump($stmt->bindColumn('name', $var));
3034
?>
3135
--CLEAN--
3236
<?php
@@ -37,5 +41,7 @@ $db = PDOTest::factory();
3741
$db->exec('DROP TABLE pdo_bindcolumn_unknown_column');
3842
?>
3943
--EXPECT--
40-
bool(false)
41-
PDOException: SQLSTATE[HY000]: General error: Did not find column name 'nosuchcolumn' in the defined columns; it will not be bound
44+
ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given
45+
ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given
46+
ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given
47+
bool(true)

‎ext/pdo_sqlite/tests/pdo_driver_options_weakref.phpt‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@ var_dump($weakReference->get());
1919

2020
$stmt = $db->prepare('SELECT ? AS value');
2121
$stmt->execute();
22-
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
2322
$value = null;
2423
$driverOptions = [new Tracked()];
2524
$weakReference = WeakReference::create($driverOptions[0]);
26-
var_dump(@$stmt->bindColumn('missing', $value, PDO::PARAM_STR, 0, $driverOptions));
27-
unset($driverOptions);
25+
try {
26+
$stmt->bindColumn('missing', $value, PDO::PARAM_STR, 0, $driverOptions);
27+
} catch (ValueError $e) {
28+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
29+
}
30+
unset($e, $driverOptions);
2831
var_dump($weakReference->get());
2932
unset($value, $stmt);
3033
?>
3134
--EXPECT--
3235
bool(true)
3336
NULL
34-
bool(false)
37+
ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "missing" given
3538
NULL

0 commit comments

Comments
 (0)