Skip to content

Commit a994067

Browse files
committed
pdo_pgsql: unbuffered fetching: test
memory & non-regression test
1 parent d0e2286 commit a994067

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

ext/pdo_pgsql/tests/gh15287.phpt

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
--TEST--
2+
PDO PgSQL #15287 (Pdo\Pgsql has no real lazy fetch mode)
3+
--EXTENSIONS--
4+
pdo
5+
pdo_pgsql
6+
--SKIPIF--
7+
<?php
8+
require __DIR__ . '/config.inc';
9+
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
15+
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
16+
$pdo = PDOTest::test_factory(__DIR__ . '/common.phpt');
17+
18+
// We need a dataset of some KB so that memory gain is significant.
19+
// See https://www.postgresql.org/message-id/1140652.1687950987%40sss.pgh.pa.us
20+
$pdo->exec("create temp table t (n int, t text)");
21+
$pdo->exec("insert into t values (0, 'original')");
22+
for ($i = -1; ++$i < 8;) {
23+
$pdo->exec("insert into t select n + 1, 'non '||t from t");
24+
}
25+
26+
$reqOf3 = 'select 79 n union all select 80 union all select 81';
27+
$reqOfBig = 'select * from t';
28+
29+
echo "=== mem test ===\n";
30+
31+
// First execute without lazy fetching, as a reference and non-regression;
32+
// execute twice: in case warmup reduces memory consumption, we want the stabilized consumption.
33+
for ($i = -1; ++$i < 3;) {
34+
switch ($i) {
35+
case 0:
36+
echo "Without lazy fetching:\n";
37+
break;
38+
case 2:
39+
echo "With lazy fetching:\n";
40+
$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
41+
break;
42+
}
43+
$stmt = $pdo->prepare($reqOfBig);
44+
$stmt->execute();
45+
$res = [];
46+
// No fetchAll because we want the memory of the result of the FORElast call (the last one is empty).
47+
while (($re = $stmt->fetch(PDO::FETCH_ASSOC))) {
48+
$res[] = $re;
49+
// Memory introspection relies on an optionally-compiled constant.
50+
if (defined('PDO::PGSQL_ATTR_RESULT_MEMORY_SIZ')) {
51+
$mem = $stmt->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE);
52+
} else {
53+
// If not there emulate a return value which validates our test.
54+
$mem = $i < 2 ? 1 : 0;
55+
}
56+
}
57+
echo "ResultSet is $mem bytes long\n";
58+
if ($i == 2) {
59+
echo "ResultSet is " . ($mem > $mem0 ? "longer" : ($mem == $mem0 ? "not shorter" : ($mem <= $mem0 / 2 ? "more than twice shorter" : "a bit shorter"))) . " than without lazy fetching\n";
60+
} else {
61+
$mem0 = $mem;
62+
}
63+
}
64+
65+
$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
66+
67+
foreach ([
68+
// @todo Understand why query only returns the first result.
69+
// The statement destructor gets called between the query and the first fetch;
70+
// as such only the first fetch (which is called during query()) stores a result,
71+
// the subsequent ones work on an invalid, freed statement.
72+
//[ 'query', 'fetch' ],
73+
//[ 'query', 'fetchAll' ],
74+
[ 'prepare', 'fetch' ],
75+
[ 'prepare', 'fetchAll' ],
76+
] as $mode) {
77+
echo "=== with " . implode(' / ', $mode). " ===\n";
78+
switch ($mode[0]) {
79+
case 'query':
80+
$stmt = $pdo->query($reqOf3);
81+
break;
82+
case 'prepare':
83+
$stmt = $pdo->prepare($reqOf3);
84+
$stmt->execute();
85+
break;
86+
}
87+
switch ($mode[1]) {
88+
case 'fetch':
89+
$res = [];
90+
while (($re = $stmt->fetch(PDO::FETCH_ASSOC))) {
91+
$res[] = $re;
92+
}
93+
break;
94+
case 'fetchAll':
95+
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
96+
break;
97+
}
98+
var_dump($res);
99+
}
100+
echo "without reading the full result set:\n";
101+
// @todo
102+
echo "DML works too:\n";
103+
// @todo
104+
?>
105+
--EXPECTF--
106+
=== mem test ===
107+
Without lazy fetching:
108+
ResultSet is %d bytes long
109+
ResultSet is %d bytes long
110+
With lazy fetching:
111+
ResultSet is %d bytes long
112+
ResultSet is more than twice shorter than without lazy fetching
113+
=== with prepare / fetch ===
114+
array(3) {
115+
[0]=>
116+
array(1) {
117+
["n"]=>
118+
string(2) "79"
119+
}
120+
[1]=>
121+
array(1) {
122+
["n"]=>
123+
string(2) "80"
124+
}
125+
[2]=>
126+
array(1) {
127+
["n"]=>
128+
string(2) "81"
129+
}
130+
}
131+
=== with prepare / fetchAll ===
132+
array(3) {
133+
[0]=>
134+
array(1) {
135+
["n"]=>
136+
string(2) "79"
137+
}
138+
[1]=>
139+
array(1) {
140+
["n"]=>
141+
string(2) "80"
142+
}
143+
[2]=>
144+
array(1) {
145+
["n"]=>
146+
string(2) "81"
147+
}
148+
}
149+
without reading the full result set:
150+
DML works too:

0 commit comments

Comments
 (0)