[php-src] PR #24565: ext/pdo: Size PDO::FETCH_BOTH rows for both keys of each column

From: Date: Thu, 22 Oct 2026 17:39:36 +0000
Subject: [php-src] PR #24565: ext/pdo: Size PDO::FETCH_BOTH rows for both keys of each column
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-39729@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/24565 Author: brainexe PDO::FETCH_BOTH is the default fetch mode. It stores every value twice, under the column name and under the column index, but do_fetch() created the row array with array_init_size(return_value, stmt->column_count), i.e. room for one entry per column. With the minimum hash size of 8, every row with 5 or more columns therefore filled its table halfway through and was resized and rehashed once, for every fetched row. This PR sizes the array for 2 * column_count entries in FETCH_BOTH mode. The array ends up with the same size as before, it just gets there without the intermediate table. Other fetch modes are unchanged. (PDO_FETCH_USE_DEFAULT is resolved to the statement's default mode at the start of do_fetch(), so the default case is covered.) ## Benchmark Release build (-O2 -g -fno-omit-frame-pointer), opcache on, JIT off, pinned to one P-core, perf stat -r5, user-space counters. Compared to master a2e4d85bf31. **pdo_pgsql**, fetchAll(PDO::FETCH_BOTH) of 10k rows with 11 columns (int8, text ×4, timestamptz ×2, numeric, int4, bool, float8; one nullable), 50 times, PostgreSQL 18 on localhost: | | master | this PR | | |---|---|---|---| | instructions | 4766M | 4534M | −4.9% | | cycles | 1408M | 1278M | −9.2% | **pdo_sqlite**, in-memory table with N columns (alternating integer and text), fetchAll(PDO::FETCH_BOTH) of 10k rows, 20 times. The numbers include creating and filling the table, so the relative gain is smaller than for the fetch alone: | columns | master | this PR | | |---|---|---|---| | 3 | 735.6M | 732.5M | ±0 (fits into 8 slots anyway) | | 4 | 933.6M | 940.8M | ±0 (noise) | | 5 | 1142.1M | 1091.1M | −4.5% | | 8 | 1678.9M | 1624.5M | −3.2% | | 11 | 2205.8M | 2111.4M | −4.3% | | 20 | 3822.2M | 3675.2M | −3.8% | Cycles move by −4.7% (5 columns) to −6.2% (20 columns). Controls: FETCH_ASSOC (pgsql 10k×11 and sqlite 11 columns) and a prepare/execute/fetch-one loop stay within the noise of ±1%. Memory: unchanged. fetchAll(FETCH_BOTH) of the 10k×11 pgsql result retains 17.00 MB and peaks at 17.42 MB on both builds. ## Verification - ext/pdo, ext/pdo_pgsql, ext/pdo_sqlite test suites pass. FETCH_BOTH is the default mode, so most existing tests exercise it, including duplicate and numeric column names. - Only the initial capacity changes; key order and contents are produced by the same code as before.

« previous php.git-pulls (#39729) next »