18
18
*/
19
19
package org .neo4j .driver .internal ;
20
20
21
- import org .neo4j .driver .v1 .*;
22
- import org .neo4j .driver .v1 .exceptions .ClientException ;
23
- import org .neo4j .driver .v1 .exceptions .NoSuchRecordException ;
24
-
25
21
import java .util .ArrayList ;
22
+ import java .util .Arrays ;
23
+ import java .util .LinkedList ;
26
24
import java .util .List ;
25
+ import java .util .Map ;
26
+ import java .util .Queue ;
27
+
28
+ import org .neo4j .driver .internal .spi .Connection ;
29
+ import org .neo4j .driver .internal .spi .StreamCollector ;
30
+ import org .neo4j .driver .internal .summary .SummaryBuilder ;
31
+ import org .neo4j .driver .v1 .Function ;
32
+ import org .neo4j .driver .v1 .Notification ;
33
+ import org .neo4j .driver .v1 .Plan ;
34
+ import org .neo4j .driver .v1 .ProfiledPlan ;
35
+ import org .neo4j .driver .v1 .Record ;
36
+ import org .neo4j .driver .v1 .RecordAccessor ;
37
+ import org .neo4j .driver .v1 .ResultCursor ;
38
+ import org .neo4j .driver .v1 .ResultSummary ;
39
+ import org .neo4j .driver .v1 .Statement ;
40
+ import org .neo4j .driver .v1 .StatementType ;
41
+ import org .neo4j .driver .v1 .UpdateStatistics ;
42
+ import org .neo4j .driver .v1 .Value ;
43
+ import org .neo4j .driver .v1 .exceptions .ClientException ;
44
+ import org .neo4j .driver .v1 .exceptions .NoSuchRecordException ;
27
45
28
46
import static java .lang .String .format ;
29
47
import static java .util .Collections .emptyList ;
48
+ import static java .util .Collections .unmodifiableMap ;
49
+ import static org .neo4j .driver .internal .ParameterSupport .NO_PARAMETERS ;
30
50
import static org .neo4j .driver .v1 .Records .recordAsIs ;
31
51
32
52
public class InternalResultCursor extends InternalRecordAccessor implements ResultCursor
33
53
{
34
- private final List <String > keys ;
35
- private final PeekingIterator <Record > iter ;
36
- private final ResultSummary summary ;
54
+ private final Connection connection ;
55
+ private final StreamCollector runResponseCollector ;
56
+ private final StreamCollector pullAllResponseCollector ;
57
+ private final Queue <Record > recordBuffer = new LinkedList <>();
58
+
59
+ private List <String > keys = null ;
60
+ private ResultSummary summary = null ;
37
61
38
62
private boolean open = true ;
39
63
private Record current = null ;
40
64
private long position = -1 ;
41
65
private long limit = -1 ;
66
+ private boolean done = false ;
42
67
43
- public InternalResultCursor ( List < String > keys , List < Record > body , ResultSummary summary )
68
+ public InternalResultCursor ( Connection connection , String statement , Map < String , Value > parameters )
44
69
{
45
- this .keys = keys ;
46
- this .iter = new PeekingIterator <>( body .iterator () );
47
- this .summary = summary ;
70
+ this .connection = connection ;
71
+
72
+ Map <String , Value > unmodifiableParameters =
73
+ (parameters == null ) || (parameters .isEmpty ()) ? NO_PARAMETERS : unmodifiableMap ( parameters );
74
+ final SummaryBuilder summaryBuilder = new SummaryBuilder ( new Statement ( statement , unmodifiableParameters ) );
75
+
76
+ this .runResponseCollector = new StreamCollector ()
77
+ {
78
+ @ Override
79
+ public void keys ( String [] names )
80
+ {
81
+ keys = new ArrayList <>( Arrays .asList ( names ) );
82
+ }
83
+
84
+ @ Override
85
+ public void record ( Value [] fields ) {}
86
+
87
+ @ Override
88
+ public void statementType ( StatementType type ) {}
89
+
90
+ @ Override
91
+ public void statementStatistics ( UpdateStatistics statistics ) {}
92
+
93
+ @ Override
94
+ public void plan ( Plan plan ) {}
95
+
96
+ @ Override
97
+ public void profile ( ProfiledPlan plan ) {}
98
+
99
+ @ Override
100
+ public void notifications ( List <Notification > notifications ) {}
101
+
102
+ @ Override
103
+ public void done ()
104
+ {
105
+ if ( keys == null )
106
+ {
107
+ keys = new ArrayList <>();
108
+ }
109
+ }
110
+ };
111
+ this .pullAllResponseCollector = new StreamCollector ()
112
+ {
113
+ @ Override
114
+ public void keys ( String [] names ) {}
115
+
116
+ @ Override
117
+ public void record ( Value [] fields )
118
+ {
119
+ recordBuffer .add ( new InternalRecord ( keys , fields ) );
120
+ }
121
+
122
+ @ Override
123
+ public void statementType ( StatementType type )
124
+ {
125
+ summaryBuilder .statementType ( type );
126
+ }
127
+
128
+ @ Override
129
+ public void statementStatistics ( UpdateStatistics statistics )
130
+ {
131
+ summaryBuilder .statementStatistics ( statistics );
132
+ }
133
+
134
+ @ Override
135
+ public void plan ( Plan plan )
136
+ {
137
+ summaryBuilder .plan ( plan );
138
+ }
139
+
140
+ @ Override
141
+ public void profile ( ProfiledPlan plan )
142
+ {
143
+ summaryBuilder .profile ( plan );
144
+ }
145
+
146
+ @ Override
147
+ public void notifications ( List <Notification > notifications )
148
+ {
149
+ summaryBuilder .notifications ( notifications );
150
+ }
151
+
152
+ @ Override
153
+ public void done () {
154
+ summary = summaryBuilder .build ();
155
+ done = true ;
156
+ }
157
+ };
158
+ }
159
+
160
+ StreamCollector runResponseCollector ()
161
+ {
162
+ return runResponseCollector ;
163
+ }
164
+
165
+ StreamCollector pullAllResponseCollector ()
166
+ {
167
+ return pullAllResponseCollector ;
48
168
}
49
169
50
170
@ Override
@@ -77,6 +197,9 @@ public int index( String key )
77
197
78
198
public List <String > keys ()
79
199
{
200
+ while (keys == null && !done ) {
201
+ connection .receiveOne ();
202
+ }
80
203
return keys ;
81
204
}
82
205
@@ -96,8 +219,8 @@ public Record record()
96
219
else
97
220
{
98
221
throw new NoSuchRecordException (
99
- "In order to access the fields of a record in a result, " +
100
- "you must first call next() to point the result to the next record in the result stream."
222
+ "In order to access the fields of a record in a result, " +
223
+ "you must first call next() to point the result to the next record in the result stream."
101
224
);
102
225
}
103
226
}
@@ -113,27 +236,51 @@ public long position()
113
236
public boolean atEnd ()
114
237
{
115
238
assertOpen ();
116
- return !iter .hasNext ();
239
+ if (!recordBuffer .isEmpty ())
240
+ {
241
+ return false ;
242
+ }
243
+ else if (done )
244
+ {
245
+ return true ;
246
+ }
247
+ else
248
+ {
249
+ while ( recordBuffer .isEmpty () && !done )
250
+ {
251
+ connection .receiveOne ();
252
+ }
253
+ return recordBuffer .isEmpty () && done ;
254
+ }
117
255
}
118
256
119
257
@ Override
120
258
public boolean next ()
121
259
{
122
260
assertOpen ();
123
- if ( iter .hasNext () )
261
+ Record nextRecord = recordBuffer .poll ();
262
+ if ( nextRecord != null )
124
263
{
125
- current = iter . next () ;
264
+ current = nextRecord ;
126
265
position += 1 ;
127
266
if ( position == limit )
128
267
{
129
268
discard ();
130
269
}
131
270
return true ;
132
271
}
133
- else
272
+ else if ( done )
134
273
{
135
274
return false ;
136
275
}
276
+ else
277
+ {
278
+ while ( recordBuffer .isEmpty () && !done )
279
+ {
280
+ connection .receiveOne ();
281
+ }
282
+ return next ();
283
+ }
137
284
}
138
285
139
286
@ Override
@@ -176,8 +323,8 @@ public Record first()
176
323
if ( position () >= 1 )
177
324
{
178
325
throw new NoSuchRecordException ( "Cannot retrieve the first record, because this result cursor has been moved already. " +
179
- "Please ensure you are not calling `first` multiple times, or are mixing it with calls " +
180
- "to `next`, `single`, `list` or any other method that changes the position of the cursor." );
326
+ "Please ensure you are not calling `first` multiple times, or are mixing it with calls " +
327
+ "to `next`, `single`, `list` or any other method that changes the position of the cursor." );
181
328
}
182
329
183
330
if ( position == 0 )
@@ -209,11 +356,11 @@ public Value first(int index) throws NoSuchRecordException
209
356
public Record single ()
210
357
{
211
358
Record first = first ();
212
- if ( iter . hasNext () )
359
+ if ( ! atEnd () )
213
360
{
214
361
throw new NoSuchRecordException ( "Expected a result with a single record, but this result contains at least one more. " +
215
- "Ensure your query returns only one record, or use `first` instead of `single` if " +
216
- "you do not care about the number of records in the result." );
362
+ "Ensure your query returns only one record, or use `first` instead of `single` if " +
363
+ "you do not care about the number of records in the result." );
217
364
}
218
365
return first ;
219
366
}
@@ -233,7 +380,24 @@ public Value single( int index ) throws NoSuchRecordException
233
380
@ Override
234
381
public Record peek ()
235
382
{
236
- return iter .peek ();
383
+ assertOpen ();
384
+ Record nextRecord = recordBuffer .peek ();
385
+ if ( nextRecord != null )
386
+ {
387
+ return nextRecord ;
388
+ }
389
+ else if ( done )
390
+ {
391
+ return null ;
392
+ }
393
+ else
394
+ {
395
+ while ( recordBuffer .isEmpty () && !done )
396
+ {
397
+ connection .receiveOne ();
398
+ }
399
+ return peek ();
400
+ }
237
401
}
238
402
239
403
@ Override
@@ -264,7 +428,7 @@ else if ( position == 0 || ( position == -1 && next() ) )
264
428
else
265
429
{
266
430
throw new ClientException (
267
- format ( "Can't retain records when cursor is not pointing at the first record (currently at position %d)" , position )
431
+ format ( "Can't retain records when cursor is not pointing at the first record (currently at position %d)" , position )
268
432
);
269
433
}
270
434
}
@@ -302,11 +466,18 @@ private void assertOpen()
302
466
303
467
private boolean isEmpty ()
304
468
{
305
- return position == -1 && ! iter . hasNext () ;
469
+ return position == -1 && recordBuffer . isEmpty () && done ;
306
470
}
307
471
308
472
private void discard ()
309
473
{
310
- iter .discard ();
474
+ assertOpen ();
475
+ recordBuffer .clear ();
476
+ while ( !done )
477
+ {
478
+ connection .receiveOne ();
479
+ recordBuffer .clear ();
480
+ }
311
481
}
482
+
312
483
}
0 commit comments