From 3e479d70ffa1d21d59273caafed0af13b4c6d404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20D=C3=B6tsch?= Date: Wed, 14 Oct 2026 20:05:27 +0200 Subject: [PATCH] ext/pdo_pgsql: Convert integer columns without strtoll() PostgreSQL sends int2/int4/int8 values in text format in canonical decimal form: an optional minus sign followed by digits without leading zeros. That is exactly what ZEND_HANDLE_NUMERIC_STR() accepts for numeric array keys, including the range check. Use it instead of calling strtoll() through ZEND_ATOL() for every value, and keep ZEND_ATOL() as fallback for anything else. fetchAll(PDO::FETCH_ASSOC) of 1M rows with 4 integer columns: -11% instructions, -9% cycles. --- ext/pdo_pgsql/pgsql_statement.c | 11 ++++- ext/pdo_pgsql/tests/fetch_integers.phpt | 56 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_pgsql/tests/fetch_integers.phpt diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 2f0725534937..6558a0304c5d 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -753,8 +753,17 @@ static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pd #if SIZEOF_ZEND_LONG >= 8 case INT8OID: #endif - ZVAL_LONG(result, ZEND_ATOL(ptr)); + { + /* PostgreSQL sends integers in canonical decimal form, which the numeric array key + * parser handles without the overhead of strtoll() */ + zend_ulong lval; + if (EXPECTED(ZEND_HANDLE_NUMERIC_STR(ptr, len, lval))) { + ZVAL_LONG(result, (zend_long) lval); + } else { + ZVAL_LONG(result, ZEND_ATOL(ptr)); + } break; + } case FLOAT4OID: case FLOAT8OID: if (strncmp(ptr, "Infinity", len) == 0) { diff --git a/ext/pdo_pgsql/tests/fetch_integers.phpt b/ext/pdo_pgsql/tests/fetch_integers.phpt new file mode 100644 index 000000000000..54287dc200ea --- /dev/null +++ b/ext/pdo_pgsql/tests/fetch_integers.phpt @@ -0,0 +1,56 @@ +--TEST-- +PDO PgSQL: integer columns at their boundaries +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +$stmt = $db->query(<<<'SQL' +SELECT 0::int2, (-1)::int2, (-32768)::int2, 32767::int2, + (-2147483648)::int4, 2147483647::int4, 7::int4, + 999999999999999999::int8, (-999999999999999999)::int8, + 1000000000000000000::int8, (-1000000000000000000)::int8, + 9223372036854775807::int8, '-9223372036854775808'::int8 +SQL); +var_dump($stmt->fetch(PDO::FETCH_NUM)); +?> +--EXPECT-- +array(13) { + [0]=> + int(0) + [1]=> + int(-1) + [2]=> + int(-32768) + [3]=> + int(32767) + [4]=> + int(-2147483648) + [5]=> + int(2147483647) + [6]=> + int(7) + [7]=> + int(999999999999999999) + [8]=> + int(-999999999999999999) + [9]=> + int(1000000000000000000) + [10]=> + int(-1000000000000000000) + [11]=> + int(9223372036854775807) + [12]=> + int(-9223372036854775808) +}