Skip to content

Commit 8aac6fb

Browse files
committed
pdo_pgsql: unbuffered fetching: test
memory & non-regression test
1 parent 3e8aab9 commit 8aac6fb

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed

ext/pdo_pgsql/tests/gh15287.phpt

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
18+
19+
// We need a dataset of several KB so that memory gain is significant.
20+
// See https://www.postgresql.org/message-id/1140652.1687950987%40sss.pgh.pa.us
21+
$pdo->exec("create temp table t (n int, t text)");
22+
$pdo->exec("insert into t values (0, 'original')");
23+
for ($i = -1; ++$i < 8;) {
24+
$pdo->exec("insert into t select n + 1, 'non '||t from t");
25+
}
26+
27+
$reqOf3 = 'select 79 n union all select 80 union all select 81';
28+
$reqOfBig = 'select * from t';
29+
30+
echo "=== mem test ===\n";
31+
32+
// First execute without lazy fetching, as a reference and non-regression;
33+
// execute twice: in case warmup reduces memory consumption, we want the stabilized consumption.
34+
for ($i = -1; ++$i < 3;) {
35+
switch ($i) {
36+
case 0:
37+
echo "Without lazy fetching:\n";
38+
break;
39+
case 2:
40+
echo "With lazy fetching:\n";
41+
$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
42+
break;
43+
}
44+
$stmt = $pdo->prepare($reqOfBig);
45+
$stmt->execute();
46+
$res = [];
47+
// No fetchAll because we want the memory of the result of the FORElast call (the last one is empty).
48+
while (($re = $stmt->fetch())) {
49+
$res[] = $re;
50+
// Memory introspection relies on an optionally-compiled constant.
51+
if (defined('PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE')) {
52+
$mem = $stmt->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE);
53+
} else {
54+
// If not there emulate a return value which validates our test.
55+
$mem = $i < 2 ? 1 : 0;
56+
}
57+
}
58+
echo "ResultSet is $mem bytes long\n";
59+
if ($i == 2) {
60+
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";
61+
} else {
62+
$mem0 = $mem;
63+
}
64+
}
65+
66+
$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
67+
68+
function display($res)
69+
{
70+
echo implode("\n", array_map(fn($row) => implode("\t", $row), $res))."\n";
71+
}
72+
73+
foreach ([
74+
[ 'query', 'fetch' ],
75+
[ 'query', 'fetchAll' ],
76+
[ 'prepare', 'fetch' ],
77+
[ 'prepare', 'fetchAll' ],
78+
] as $mode) {
79+
echo "=== with " . implode(' / ', $mode). " ===\n";
80+
switch ($mode[0]) {
81+
case 'query':
82+
$stmt = $pdo->query($reqOf3);
83+
break;
84+
case 'prepare':
85+
$stmt = $pdo->prepare($reqOf3);
86+
$stmt->execute();
87+
break;
88+
}
89+
switch ($mode[1]) {
90+
case 'fetch':
91+
$res = [];
92+
while (($re = $stmt->fetch())) {
93+
$res[] = $re;
94+
}
95+
break;
96+
case 'fetchAll':
97+
$res = $stmt->fetchAll();
98+
break;
99+
}
100+
display($res);
101+
}
102+
echo "DML works too:\n";
103+
$pdo->exec("create temp table t2 as select 678 n, 'ok' status");
104+
echo "multiple calls to the same prepared statement, some interrupted before having read all results:\n";
105+
$stmt = $pdo->prepare("select :1 n union all select :1 + 1 union all select :1 + 2 union all select :1 + 3");
106+
$stmt->execute([ 32 ]);
107+
$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
108+
$stmt->execute([ 15 ]);
109+
$res = []; while (($re = $stmt->fetch())) $res[] = $re; display($res);
110+
$stmt->execute([ 0 ]);
111+
$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
112+
display($pdo->query("select * from t2")->fetchAll());
113+
?>
114+
--EXPECTF--
115+
=== mem test ===
116+
Without lazy fetching:
117+
ResultSet is %d bytes long
118+
ResultSet is %d bytes long
119+
With lazy fetching:
120+
ResultSet is %d bytes long
121+
ResultSet is more than twice shorter than without lazy fetching
122+
=== with query / fetch ===
123+
79
124+
80
125+
81
126+
=== with query / fetchAll ===
127+
79
128+
80
129+
81
130+
=== with prepare / fetch ===
131+
79
132+
80
133+
81
134+
=== with prepare / fetchAll ===
135+
79
136+
80
137+
81
138+
DML works too:
139+
multiple calls to the same prepared statement, some interrupted before having read all results:
140+
32
141+
33
142+
15
143+
16
144+
17
145+
18
146+
0
147+
1
148+
678 ok

0 commit comments

Comments
 (0)