Skip to content

Commit ed0339f

Browse files
committed
Fix GH-20214: PDO::FETCH_DEFAULT unexpected behavior with PDOStatement::setFetchMode
When setFetchMode(PDO::FETCH_DEFAULT) is called, mode=0 (PDO_FETCH_USE_DEFAULT) gets stored as the statement's default fetch type. Later, do_fetch() tries to resolve PDO_FETCH_USE_DEFAULT by reading stmt->default_fetch_type, which is also 0 — circular reference that on 8.4 silently fell through to FETCH_BOTH and on master throws a ValueError. Resolve PDO_FETCH_USE_DEFAULT to the connection-level default early in pdo_stmt_setup_fetch_mode(), before flags extraction and the mode switch, so the rest of the function processes the actual fetch mode. Closes GH-20214
1 parent 9f33bff commit ed0339f

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

ext/pdo/pdo_stmt.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,10 @@ bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_a
17311731

17321732
stmt->default_fetch_type = PDO_FETCH_BOTH;
17331733

1734+
if ((mode & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1735+
mode = stmt->dbh->default_fetch_type;
1736+
}
1737+
17341738
flags = mode & PDO_FETCH_FLAGS;
17351739

17361740
if (!pdo_stmt_verify_mode(stmt, mode, mode_arg_num, false)) {

ext/pdo_sqlite/tests/gh20214.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
GH-20214 (PDO::FETCH_DEFAULT unexpected behavior with PDOStatement::setFetchMode)
3+
--EXTENSIONS--
4+
pdo_sqlite
5+
--FILE--
6+
<?php
7+
$db = new PDO("sqlite::memory:");
8+
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
9+
10+
// setFetchMode with FETCH_DEFAULT should use connection default (FETCH_OBJ)
11+
$stmt = $db->query("SELECT 'v1' AS c1, 'v2' AS c2");
12+
$stmt->setFetchMode(PDO::FETCH_DEFAULT);
13+
$row = $stmt->fetch();
14+
var_dump($row instanceof stdClass);
15+
var_dump($row->c1);
16+
17+
// fetch with FETCH_DEFAULT should also use connection default
18+
$stmt = $db->query("SELECT 'v1' AS c1, 'v2' AS c2");
19+
$row = $stmt->fetch(PDO::FETCH_DEFAULT);
20+
var_dump($row instanceof stdClass);
21+
22+
// fetchAll with FETCH_DEFAULT should also use connection default
23+
$stmt = $db->query("SELECT 'v1' AS c1, 'v2' AS c2");
24+
$rows = $stmt->fetchAll(PDO::FETCH_DEFAULT);
25+
var_dump($rows[0] instanceof stdClass);
26+
27+
// setFetchMode then fetch without argument
28+
$stmt = $db->query("SELECT 'v1' AS c1, 'v2' AS c2");
29+
$stmt->setFetchMode(PDO::FETCH_DEFAULT);
30+
$row = $stmt->fetch();
31+
var_dump($row instanceof stdClass);
32+
?>
33+
--EXPECT--
34+
bool(true)
35+
string(2) "v1"
36+
bool(true)
37+
bool(true)
38+
bool(true)

0 commit comments

Comments
 (0)