15
15
*/
16
16
abstract class PooledResult implements Result, \IteratorAggregate
17
17
{
18
- /** @var null|\Closure():void */
19
- private ?\Closure $ release ;
20
-
21
18
/** @var Future<TResult|null>|null */
22
19
private ?Future $ next = null ;
23
20
21
+ /** @var \Iterator<int, array<string, TFieldValue>> */
22
+ private readonly \Iterator $ iterator ;
23
+
24
+ /**
25
+ * @template Tr of Result
26
+ *
27
+ * @param Tr $result
28
+ * @param \Closure():void $release
29
+ *
30
+ * @return Tr
31
+ */
32
+ abstract protected static function newInstanceFrom (Result $ result , \Closure $ release ): Result ;
33
+
24
34
/**
25
35
* @param TResult $result Result object created by pooled connection or statement.
26
36
* @param \Closure():void $release Callable to be invoked when the result set is destroyed.
27
37
*/
28
- public function __construct (private readonly Result $ result , \Closure $ release )
38
+ public function __construct (private readonly Result $ result , private readonly \Closure $ release )
29
39
{
30
- $ this ->release = $ release ;
31
-
32
40
if ($ this ->result instanceof CommandResult) {
33
- $ this ->next = $ this ->fetchNextResult ();
41
+ $ this ->iterator = $ this ->result ->getIterator ();
42
+ $ this ->next = self ::fetchNextResult ($ this ->result , $ this ->release );
43
+ return ;
34
44
}
45
+
46
+ $ next = &$ this ->next ;
47
+ $ this ->iterator = (static function () use (&$ next , $ result , $ release ): \Generator {
48
+ try {
49
+ yield from $ result ;
50
+ } catch (\Throwable $ exception ) {
51
+ if (!$ next ) {
52
+ EventLoop::queue ($ release );
53
+ }
54
+ throw $ exception ;
55
+ }
56
+
57
+ $ next ??= self ::fetchNextResult ($ result , $ release );
58
+ })();
35
59
}
36
60
37
61
public function __destruct ()
38
62
{
39
- $ this ->dispose ( );
63
+ EventLoop:: queue ( self :: dispose (...), $ this ->iterator );
40
64
}
41
65
42
- /**
43
- * @param TResult $result
44
- * @param \Closure():void $release
45
- *
46
- * @return TResult
47
- */
48
- abstract protected function newInstanceFrom (Result $ result , \Closure $ release ): Result ;
49
-
50
- private function dispose (): void
66
+ private static function dispose (\Iterator $ iterator ): void
51
67
{
52
- if ($ this ->release !== null ) {
53
- EventLoop::queue ($ this ->release );
54
- $ this ->release = null ;
68
+ try {
69
+ // Discard remaining rows in the result set.
70
+ while ($ iterator ->valid ()) {
71
+ $ iterator ->next ();
72
+ }
73
+ } catch (\Throwable ) {
74
+ // Ignore errors while discarding result.
55
75
}
56
76
}
57
77
58
78
public function getIterator (): \Traversable
59
79
{
60
- try {
61
- yield from $ this ->result ;
62
- } catch (\Throwable $ exception ) {
63
- $ this ->dispose ();
64
- throw $ exception ;
65
- }
80
+ return $ this ->iterator ;
66
81
}
67
82
68
83
public function fetchRow (): ?array
69
84
{
70
- return $ this ->result ->fetchRow ();
85
+ if (!$ this ->iterator ->valid ()) {
86
+ return null ;
87
+ }
88
+
89
+ $ current = $ this ->iterator ->current ();
90
+ $ this ->iterator ->next ();
91
+ return $ current ;
71
92
}
72
93
73
94
public function getRowCount (): ?int
@@ -85,24 +106,30 @@ public function getColumnCount(): ?int
85
106
*/
86
107
public function getNextResult (): ?Result
87
108
{
88
- return ($ this ->next ??= $ this ->fetchNextResult ())->await ();
109
+ $ this ->next ??= self ::fetchNextResult ($ this ->result , $ this ->release );
110
+ return $ this ->next ->await ();
89
111
}
90
112
91
- private function fetchNextResult (): Future
113
+ /**
114
+ * @template Tr of Result
115
+ *
116
+ * @param Tr $result
117
+ * @param \Closure():void $release
118
+ *
119
+ * @return Future<Tr|null>
120
+ */
121
+ private static function fetchNextResult (Result $ result , \Closure $ release ): Future
92
122
{
93
- return async (function (): ?Result {
94
- /** @var TResult |null $result */
95
- $ result = $ this -> result ->getNextResult ();
123
+ return async (static function () use ( $ result , $ release ): ?Result {
124
+ /** @var Tr |null $result */
125
+ $ result = $ result ->getNextResult ();
96
126
97
- if ($ result === null || $ this -> release === null ) {
98
- $ this -> dispose ( );
127
+ if ($ result === null ) {
128
+ EventLoop:: queue ( $ release );
99
129
return null ;
100
130
}
101
131
102
- $ result = $ this ->newInstanceFrom ($ result , $ this ->release );
103
- $ this ->release = null ;
104
-
105
- return $ result ;
132
+ return static ::newInstanceFrom ($ result , $ release );
106
133
});
107
134
}
108
135
}
0 commit comments