ru_php/ext/pdo_pgsql/tests/gh15287.phpt

184 lines
4.8 KiB
PHP

--TEST--
PDO PgSQL #15287 (Pdo\Pgsql has no real lazy fetch mode)
--EXTENSIONS--
pdo
pdo_pgsql
--SKIPIF--
<?php
require __DIR__ . '/config.inc';
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
PDOTest::skip();
?>
--FILE--
<?php
require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
$pdo = PDOTest::test_factory(__DIR__ . '/common.phpt');
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
// We need a dataset of several KB so that memory gain is significant.
// See https://www.postgresql.org/message-id/1140652.1687950987%40sss.pgh.pa.us
$pdo->exec("create temp table t (n int, t text)");
$pdo->exec("insert into t values (0, 'original')");
for ($i = -1; ++$i < 8;) {
$pdo->exec("insert into t select n + 1, 'non '||t from t");
}
$reqOf3 = 'select 79 n union all select 80 union all select 81';
$reqOfBig = 'select * from t';
function display($res)
{
echo implode("\n", array_map(fn($row) => implode("\t", $row), $res))."\n";
}
echo "=== non regression ===\n";
// libpq explicitely requires single-row-mode statements to run one at a time (one stmt must
// be fully read, or aborted, before another one can be launched).
// Ensure that integration does not break the ability of the traditional, prefetched mode,
// to mix fetching of multiple statements' result.
$stmt1 = $pdo->query($reqOf3);
$stmt2 = $pdo->query("select * from ($reqOf3) t order by n desc");
for ($i = -1; ++$i < 3;) {
display([ $stmt1->fetch() ]);
display([ $stmt2->fetch() ]);
}
echo "=== mem test ===\n";
// First execute without lazy fetching, as a reference and non-regression;
// execute twice: in case warmup reduces memory consumption, we want the stabilized consumption.
for ($i = -1; ++$i < 5;) {
$attrs = [];
$lazy = false;
switch ($i) {
case 0:
case 3:
echo "Without lazy fetching:\n";
break;
case 2:
echo "With statement-scoped lazy fetching:\n";
$attrs = [ PDO::ATTR_PREFETCH => 0 ];
$lazy = true;
break;
case 4:
echo "With connection-scoped lazy fetching:\n";
$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
$lazy = true;
break;
}
$stmt = $pdo->prepare($reqOfBig, $attrs);
$stmt->execute();
$res = [];
// No fetchAll because we want the memory of the result of the FORElast call (the last one is empty).
while (($re = $stmt->fetch())) {
$res[] = $re;
// Memory introspection relies on an optionally-compiled constant.
if (defined('Pdo\Pgsql::ATTR_RESULT_MEMORY_SIZE')) {
$mem = $stmt->getAttribute(Pdo\Pgsql::ATTR_RESULT_MEMORY_SIZE);
} else {
// If not there emulate a return value which validates our test.
$mem = $lazy ? 0 : 1;
}
}
echo "ResultSet is $mem bytes long\n";
if ($i >= 2) {
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";
} else {
$mem0 = $mem;
}
}
$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
foreach ([
[ 'query', 'fetch' ],
[ 'query', 'fetchAll' ],
[ 'prepare', 'fetch' ],
[ 'prepare', 'fetchAll' ],
] as $mode) {
echo "=== with " . implode(' / ', $mode). " ===\n";
switch ($mode[0]) {
case 'query':
$stmt = $pdo->query($reqOf3);
break;
case 'prepare':
$stmt = $pdo->prepare($reqOf3);
$stmt->execute();
break;
}
switch ($mode[1]) {
case 'fetch':
$res = [];
while (($re = $stmt->fetch())) {
$res[] = $re;
}
break;
case 'fetchAll':
$res = $stmt->fetchAll();
break;
}
display($res);
}
echo "DML works too:\n";
$pdo->exec("create temp table t2 as select 678 n, 'ok' status");
echo "multiple calls to the same prepared statement, some interrupted before having read all results:\n";
$stmt = $pdo->prepare("select :1 n union all select :1 + 1 union all select :1 + 2 union all select :1 + 3");
$stmt->execute([ 32 ]);
$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
$stmt->execute([ 15 ]);
$res = []; while (($re = $stmt->fetch())) $res[] = $re; display($res);
$stmt->execute([ 0 ]);
$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
display($pdo->query("select * from t2")->fetchAll());
?>
--EXPECTF--
=== non regression ===
79
81
80
80
81
79
=== mem test ===
Without lazy fetching:
ResultSet is %d bytes long
ResultSet is %d bytes long
With statement-scoped lazy fetching:
ResultSet is %d bytes long
ResultSet is more than twice shorter than without lazy fetching
Without lazy fetching:
ResultSet is %d bytes long
ResultSet is not shorter than without lazy fetching
With connection-scoped lazy fetching:
ResultSet is %d bytes long
ResultSet is more than twice shorter than without lazy fetching
=== with query / fetch ===
79
80
81
=== with query / fetchAll ===
79
80
81
=== with prepare / fetch ===
79
80
81
=== with prepare / fetchAll ===
79
80
81
DML works too:
multiple calls to the same prepared statement, some interrupted before having read all results:
32
33
15
16
17
18
0
1
678 ok