xref: /php-src/ext/pdo_pgsql/tests/gh15287.phpt (revision 68537fd9)
1--TEST--
2PDO PgSQL #15287 (Pdo\Pgsql has no real lazy fetch mode)
3--EXTENSIONS--
4pdo
5pdo_pgsql
6--SKIPIF--
7<?php
8require __DIR__ . '/config.inc';
9require  __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
10PDOTest::skip();
11?>
12--FILE--
13<?php
14
15require  __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')");
23for ($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
30function display($res)
31{
32	echo implode("\n", array_map(fn($row) => implode("\t", $row), $res))."\n";
33}
34
35echo "=== non regression ===\n";
36
37// libpq explicitely requires single-row-mode statements to run one at a time (one stmt must
38// be fully read, or aborted, before another one can be launched).
39// Ensure that integration does not break the ability of the traditional, prefetched mode,
40// to mix fetching of multiple statements' result.
41$stmt1 = $pdo->query($reqOf3);
42$stmt2 = $pdo->query("select * from ($reqOf3) t order by n desc");
43for ($i = -1; ++$i < 3;) {
44	display([ $stmt1->fetch() ]);
45	display([ $stmt2->fetch() ]);
46}
47
48echo "=== mem test ===\n";
49
50// First execute without lazy fetching, as a reference and non-regression;
51// execute twice: in case warmup reduces memory consumption, we want the stabilized consumption.
52for ($i = -1; ++$i < 5;) {
53	$attrs = [];
54	$lazy = false;
55	switch ($i) {
56		case 0:
57		case 3:
58			echo "Without lazy fetching:\n";
59			break;
60		case 2:
61			echo "With statement-scoped lazy fetching:\n";
62			$attrs = [ PDO::ATTR_PREFETCH => 0 ];
63			$lazy = true;
64			break;
65		case 4:
66			echo "With connection-scoped lazy fetching:\n";
67			$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
68			$lazy = true;
69			break;
70	}
71	$stmt = $pdo->prepare($reqOfBig, $attrs);
72	$stmt->execute();
73	$res = [];
74	// No fetchAll because we want the memory of the result of the FORElast call (the last one is empty).
75	while (($re = $stmt->fetch())) {
76		$res[] = $re;
77		// Memory introspection relies on an optionally-compiled constant.
78		if (defined('PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE')) {
79			$mem = $stmt->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE);
80		} else {
81			// If not there emulate a return value which validates our test.
82			$mem = $lazy ? 0 : 1;
83		}
84	}
85	echo "ResultSet is $mem bytes long\n";
86	if ($i >= 2) {
87		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";
88	} else {
89		$mem0 = $mem;
90	}
91}
92
93$pdo->setAttribute(PDO::ATTR_PREFETCH, 0);
94
95foreach ([
96	[ 'query', 'fetch' ],
97	[ 'query', 'fetchAll' ],
98	[ 'prepare', 'fetch' ],
99	[ 'prepare', 'fetchAll' ],
100] as $mode) {
101	echo "=== with " . implode(' / ', $mode). " ===\n";
102	switch ($mode[0]) {
103		case 'query':
104			$stmt = $pdo->query($reqOf3);
105			break;
106		case 'prepare':
107			$stmt = $pdo->prepare($reqOf3);
108			$stmt->execute();
109			break;
110	}
111	switch ($mode[1]) {
112		case 'fetch':
113			$res = [];
114			while (($re = $stmt->fetch())) {
115				$res[] = $re;
116			}
117			break;
118		case 'fetchAll':
119			$res = $stmt->fetchAll();
120			break;
121	}
122	display($res);
123}
124echo "DML works too:\n";
125$pdo->exec("create temp table t2 as select 678 n, 'ok' status");
126echo "multiple calls to the same prepared statement, some interrupted before having read all results:\n";
127$stmt = $pdo->prepare("select :1 n union all select :1 + 1 union all select :1 + 2 union all select :1 + 3");
128$stmt->execute([ 32 ]);
129$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
130$stmt->execute([ 15 ]);
131$res = []; while (($re = $stmt->fetch())) $res[] = $re; display($res);
132$stmt->execute([ 0 ]);
133$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
134display($pdo->query("select * from t2")->fetchAll());
135?>
136--EXPECTF--
137=== non regression ===
13879
13981
14080
14180
14281
14379
144=== mem test ===
145Without lazy fetching:
146ResultSet is %d bytes long
147ResultSet is %d bytes long
148With statement-scoped lazy fetching:
149ResultSet is %d bytes long
150ResultSet is more than twice shorter than without lazy fetching
151Without lazy fetching:
152ResultSet is %d bytes long
153ResultSet is not shorter than without lazy fetching
154With connection-scoped lazy fetching:
155ResultSet is %d bytes long
156ResultSet is more than twice shorter than without lazy fetching
157=== with query / fetch ===
15879
15980
16081
161=== with query / fetchAll ===
16279
16380
16481
165=== with prepare / fetch ===
16679
16780
16881
169=== with prepare / fetchAll ===
17079
17180
17281
173DML works too:
174multiple calls to the same prepared statement, some interrupted before having read all results:
17532
17633
17715
17816
17917
18018
1810
1821
183678	ok
184