Skip to content

Commit c0018be

Browse files
brainexedevnexen
authored andcommitted
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. Close GH-24385
1 parent a59b0ca commit c0018be

4 files changed

Lines changed: 68 additions & 1 deletion

File tree

‎NEWS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ PHP NEWS
9191
- PDO_PGSQL:
9292
. Improved performance of the SQL parser that handles placeholders.
9393
(brainexe)
94+
. Improved performance of fetching integer columns. (brainexe)
9495

9596
- PGSQL:
9697
. Fixed pg_lo_write() rejecting data containing null bytes. (Ilia Alshanetsky)

‎UPGRADING‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,4 @@ PHP 8.7 UPGRADE NOTES
176176
- PDO_PGSQL:
177177
. Improved performance of the SQL parser that handles placeholders in
178178
PDO::prepare() and, with emulated prepares, PDOStatement::execute().
179+
. Improved performance of fetching integer columns.

‎ext/pdo_pgsql/pgsql_statement.c‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,17 @@ static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pd
758758
#if SIZEOF_ZEND_LONG >= 8
759759
case INT8OID:
760760
#endif
761-
ZVAL_LONG(result, ZEND_ATOL(ptr));
761+
{
762+
/* PostgreSQL sends integers in canonical decimal form, which the numeric array key
763+
* parser handles without the overhead of strtoll() */
764+
zend_ulong lval;
765+
if (EXPECTED(ZEND_HANDLE_NUMERIC_STR(ptr, len, lval))) {
766+
ZVAL_LONG(result, (zend_long) lval);
767+
} else {
768+
ZVAL_LONG(result, ZEND_ATOL(ptr));
769+
}
762770
break;
771+
}
763772
case FLOAT4OID:
764773
case FLOAT8OID:
765774
if (strncmp(ptr, "Infinity", len) == 0) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
PDO PgSQL: integer columns at their boundaries
3+
--EXTENSIONS--
4+
pdo_pgsql
5+
--SKIPIF--
6+
<?php
7+
if (PHP_INT_SIZE < 8) die('skip 64-bit only');
8+
require __DIR__ . '/config.inc';
9+
require dirname(__DIR__, 2) . '/pdo/tests/pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
15+
$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
16+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
17+
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
18+
19+
$stmt = $db->query(<<<'SQL'
20+
SELECT 0::int2, (-1)::int2, (-32768)::int2, 32767::int2,
21+
(-2147483648)::int4, 2147483647::int4, 7::int4,
22+
999999999999999999::int8, (-999999999999999999)::int8,
23+
1000000000000000000::int8, (-1000000000000000000)::int8,
24+
9223372036854775807::int8, '-9223372036854775808'::int8
25+
SQL);
26+
var_dump($stmt->fetch(PDO::FETCH_NUM));
27+
?>
28+
--EXPECT--
29+
array(13) {
30+
[0]=>
31+
int(0)
32+
[1]=>
33+
int(-1)
34+
[2]=>
35+
int(-32768)
36+
[3]=>
37+
int(32767)
38+
[4]=>
39+
int(-2147483648)
40+
[5]=>
41+
int(2147483647)
42+
[6]=>
43+
int(7)
44+
[7]=>
45+
int(999999999999999999)
46+
[8]=>
47+
int(-999999999999999999)
48+
[9]=>
49+
int(1000000000000000000)
50+
[10]=>
51+
int(-1000000000000000000)
52+
[11]=>
53+
int(9223372036854775807)
54+
[12]=>
55+
int(-9223372036854775808)
56+
}

0 commit comments

Comments
 (0)