1414namespace Laudis \Neo4j \Bolt ;
1515
1616use function array_key_exists ;
17- use function array_splice ;
1817
1918use Bolt \error \BoltException ;
2019use Bolt \error \ConnectException as BoltConnectException ;
21-
22- use function count ;
23-
2420use Generator ;
2521
2622use function in_array ;
2723use function is_array ;
2824
2925use Iterator ;
26+ use Laudis \Neo4j \Exception \Neo4jException ;
3027use Laudis \Neo4j \Formatter \SummarizedResultFormatter ;
3128use Throwable ;
3229
3330/**
3431 * @psalm-import-type BoltCypherStats from SummarizedResultFormatter
3532 *
36- * @implements Iterator<int, list< mixed>>
33+ * @implements Iterator<int, array<array-key, mixed>>
3734 */
3835final class BoltResult implements Iterator
3936{
40- /** @var list<list > */
37+ /** @var list<array<array-key, mixed> > */
4138 private array $ rows = [];
4239 private ?array $ meta = null ;
40+ private ?Neo4jException $ deferredFailure = null ;
4341 /** @var list<(callable(array):void)> */
4442 private array $ finishedCallbacks = [];
4543
@@ -92,7 +90,7 @@ public function addFinishedCallback(callable $finishedCallback): void
9290 }
9391
9492 /**
95- * @return Generator<int, list >
93+ * @return Generator<int, array<array-key, mixed> >
9694 */
9795 public function getIt (): Generator
9896 {
@@ -104,7 +102,7 @@ public function getIt(): Generator
104102 }
105103
106104 /**
107- * @return Generator<int, list< mixed>>
105+ * @return Generator<int, array<array-key, mixed>>
108106 */
109107 public function iterator (): Generator
110108 {
@@ -143,24 +141,32 @@ public function consume(): array
143141
144142 private function fetchResults (): void
145143 {
144+ if ($ this ->deferredFailure !== null ) {
145+ throw $ this ->deferredFailure ;
146+ }
147+
146148 $ this ->networkPullOccurred = true ;
147149
148150 try {
149- $ meta = $ this ->connection ->pull ($ this ->qid , $ this ->effectivePullSize ());
151+ $ pullResult = $ this ->connection ->pull ($ this ->qid , $ this ->effectivePullSize ());
150152 } catch (BoltConnectException |BoltException $ e ) {
151153 // Invalidate connection on socket/network errors so pool does not reuse it.
152154 // Rethrow as-is - Session retry logic inspects the actual exception via isConnectionError().
153155 $ this ->connection ->invalidate ();
154156 throw $ e ;
155157 }
156158 // Neo4jException and other Throwable propagate naturally - no invalidate needed for server errors
157- // $meta is non-empty: {@see BoltConnection::pull()} is contractually non-empty-list<list>.
158159
159- /** @var list<list> $rows */
160- $ rows = array_splice ($ meta , 0 , count ($ meta ) - 1 );
161- $ this ->rows = $ rows ;
160+ $ this ->rows = $ pullResult ->getRecordRows ();
161+
162+ $ deferredFailure = $ pullResult ->getDeferredFailure ();
163+ if ($ deferredFailure !== null ) {
164+ $ this ->deferredFailure = $ deferredFailure ;
165+
166+ return ;
167+ }
162168
163- $ summarySlot = $ meta [ 0 ] ?? null ;
169+ $ summarySlot = $ pullResult -> getSummary () ;
164170 if (!is_array ($ summarySlot )) {
165171 // No summary received (connection closed before summary)
166172 $ this ->meta = null ;
@@ -169,7 +175,7 @@ private function fetchResults(): void
169175 }
170176
171177 $ summaryEmpty = $ summarySlot === [];
172- $ hasDataRows = $ rows !== [];
178+ $ hasDataRows = $ this -> rows !== [];
173179
174180 if ($ summaryEmpty && !$ hasDataRows ) {
175181 // Normal completion with no records
@@ -188,7 +194,7 @@ private function fetchResults(): void
188194 /**
189195 * @psalm-suppress InvalidNullableReturnType
190196 *
191- * @return list< mixed>
197+ * @return array<array-key, mixed>
192198 */
193199 public function current (): array
194200 {
0 commit comments