[php-src] Issue #22720: fetchAll(PDO::FETCH_KEY_PAIR) returning only the last row when data contains non-unique values

[email protected] (jeromio) Tue, 14 Jul 2026 03:13:20 +0000
Newsgroups php.bugs
Message-ID <[email protected]>
Issue: https://github.com/php/php-src/issues/22720
Author: jeromio

### Description

The following code:

```
<?php
require_once('../config.php');
global $pdo;
function init_db(&$pdo) {
    //create db connection
    try {
        $pdo = new PDO('mysql:host=' . db_host . ';dbname=' . db_name . ';charset=' . db_charset, db_user, db_pass,
            [
                PDO::ATTR_TIMEOUT => 3,
                PDO::ATTR_PERSISTENT => true,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
    } catch (PDOException $exception) {
        // If there is an error with the connection, stop the script and display the error.
        exit('Failed to connect to database: ' . $exception->getMessage());
    }
}
function setup($table)
{
    global $pdo;
    $drop_table = true;
    $row_count = 10;
    if ($drop_table) {
        $q = "DROP TABLE IF EXISTS $table";
        $stmt = $pdo->prepare($q);
        $ret = $stmt->execute();
    }
    $query = "CREATE TABLE if not exists " . $table . "(
        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
          `test_id` bigint(20) unsigned NOT NULL,
          `other_id` bigint(20) unsigned NOT NULL,			  
          PRIMARY KEY (`id`),
          UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE );";
    $stmt = $pdo->prepare($query);
    $ret = $stmt->execute();
    $stmt = $pdo->prepare("INSERT INTO `$table` (`test_id`, `other_id`) VALUES (?,?)");
    for($i = 0; $i < $row_count; $i++) {
        $ret = $stmt->execute([$i > $row_count/2 ? 20 : 10, $i + 1]);
    }
}
init_db($pdo);
$table = "testing";
setup($table);
$stmt = $pdo->prepare("SELECT test_id, other_id FROM `$table` where test_id = 10");
$ret = $stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
var_dump($results);
?>
```
Resulted in this output:
```
array(1) {
  [10]=>
  int(6)
}
```

But I expected this output instead:
```
array(6) {
  [10]=>
  int(1)
[10]=>
  int(2)
[10]=>
  int(3)
[10]=>
  int(4)
[10]=>
  int(5)
[10]=>
  int(6)
}
```

When the PDO::FETCH_ASSOC is used, we get the full result set.
There is this in the [docs](https://www.php.net/manual/en/pdo.constants.fetch-modes.php#pdo.constants.fetch-key-pair):
"Note: If the first column is not unique, values will be lost. Which value(s) are lost should be considered undefined."
But then, this seems (to me) to mean that FETCH_KEY_PAIR only applies to queries where the 1st column is an index, which is pretty limiting and not really evident to users of this API. In other words, it seems not ideal to have a data access API that expects users to know the particular contents of the database.

### PHP Version

```plain
PHP 8.4.22 (cli) (built: Jun  6 2026 06:34:49) (NTS)
Copyright (c) The PHP Group
Built by Ubuntu
Zend Engine v4.4.22, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.22, Copyright (c), by Zend Technologies
    with Xdebug v3.5.0, Copyright (c) 2002-2025, by Derick Rethans
```

### Operating System

Ubuntu 24.04