@@ -2,7 +2,6 @@ import NIOCore
2
2
import Logging
3
3
4
4
final class PSQLRowStream {
5
-
6
5
enum RowSource {
7
6
case stream( PSQLRowsDataSource )
8
7
case noRows( Result < String , Error > )
@@ -11,23 +10,21 @@ final class PSQLRowStream {
11
10
let eventLoop : EventLoop
12
11
let logger : Logger
13
12
14
- private enum UpstreamState {
13
+ private enum BufferState {
15
14
case streaming( buffer: CircularBuffer < DataRow > , dataSource: PSQLRowsDataSource )
16
15
case finished( buffer: CircularBuffer < DataRow > , commandTag: String )
17
16
case failure( Error )
18
- case consumed( Result < String , Error > )
19
- case modifying
20
17
}
21
18
22
19
private enum DownstreamState {
23
- case iteratingRows( onRow: ( PSQLRow ) throws -> ( ) , EventLoopPromise < Void > )
24
- case waitingForAll( EventLoopPromise < [ PSQLRow ] > )
25
- case consuming
20
+ case waitingForConsumer( BufferState )
21
+ case iteratingRows( onRow: ( PSQLRow ) throws -> ( ) , EventLoopPromise < Void > , PSQLRowsDataSource )
22
+ case waitingForAll( [ PSQLRow ] , EventLoopPromise < [ PSQLRow ] > , PSQLRowsDataSource )
23
+ case consumed( Result < String , Error > )
26
24
}
27
25
28
26
internal let rowDescription : [ RowDescription . Column ]
29
27
private let lookupTable : [ String : Int ]
30
- private var upstreamState : UpstreamState
31
28
private var downstreamState : DownstreamState
32
29
private let jsonDecoder : PSQLJSONDecoder
33
30
@@ -36,30 +33,33 @@ final class PSQLRowStream {
36
33
eventLoop: EventLoop ,
37
34
rowSource: RowSource )
38
35
{
39
- let buffer = CircularBuffer < DataRow > ( )
40
-
41
- self . downstreamState = . consuming
36
+ let bufferState : BufferState
42
37
switch rowSource {
43
38
case . stream( let dataSource) :
44
- self . upstreamState = . streaming( buffer: buffer , dataSource: dataSource)
39
+ bufferState = . streaming( buffer: . init ( ) , dataSource: dataSource)
45
40
case . noRows( . success( let commandTag) ) :
46
- self . upstreamState = . finished( buffer: . init( ) , commandTag: commandTag)
41
+ bufferState = . finished( buffer: . init( ) , commandTag: commandTag)
47
42
case . noRows( . failure( let error) ) :
48
- self . upstreamState = . failure( error)
43
+ bufferState = . failure( error)
49
44
}
50
45
46
+ self . downstreamState = . waitingForConsumer( bufferState)
47
+
51
48
self . eventLoop = eventLoop
52
49
self . logger = queryContext. logger
53
50
self . jsonDecoder = queryContext. jsonDecoder
54
51
55
52
self . rowDescription = rowDescription
53
+
56
54
var lookup = [ String: Int] ( )
57
55
lookup. reserveCapacity ( rowDescription. count)
58
56
rowDescription. enumerated ( ) . forEach { ( index, column) in
59
57
lookup [ column. name] = index
60
58
}
61
59
self . lookupTable = lookup
62
60
}
61
+
62
+ // MARK: Consume in array
63
63
64
64
func all( ) -> EventLoopFuture < [ PSQLRow ] > {
65
65
if self . eventLoop. inEventLoop {
@@ -74,40 +74,37 @@ final class PSQLRowStream {
74
74
private func all0( ) -> EventLoopFuture < [ PSQLRow ] > {
75
75
self . eventLoop. preconditionInEventLoop ( )
76
76
77
- guard case . consuming = self . downstreamState else {
78
- preconditionFailure ( " Invalid state " )
77
+ guard case . waitingForConsumer ( let bufferState ) = self . downstreamState else {
78
+ preconditionFailure ( " Invalid state: \( self . downstreamState ) " )
79
79
}
80
80
81
- switch self . upstreamState {
82
- case . streaming( _, let dataSource) :
83
- dataSource. request ( for: self )
81
+ switch bufferState {
82
+ case . streaming( let bufferedRows, let dataSource) :
84
83
let promise = self . eventLoop. makePromise ( of: [ PSQLRow ] . self)
85
- self . downstreamState = . waitingForAll( promise)
84
+ let rows = bufferedRows. map { data in
85
+ PSQLRow ( data: data, lookupTable: self . lookupTable, columns: self . rowDescription, jsonDecoder: self . jsonDecoder)
86
+ }
87
+ self . downstreamState = . waitingForAll( rows, promise, dataSource)
88
+ // immediately request more
89
+ dataSource. request ( for: self )
86
90
return promise. futureResult
87
91
88
92
case . finished( let buffer, let commandTag) :
89
- self . upstreamState = . modifying
90
-
91
93
let rows = buffer. map {
92
94
PSQLRow ( data: $0, lookupTable: self . lookupTable, columns: self . rowDescription, jsonDecoder: self . jsonDecoder)
93
95
}
94
96
95
- self . downstreamState = . consuming
96
- self . upstreamState = . consumed( . success( commandTag) )
97
+ self . downstreamState = . consumed( . success( commandTag) )
97
98
return self . eventLoop. makeSucceededFuture ( rows)
98
99
99
- case . consumed:
100
- preconditionFailure ( " We already signaled, that the stream has completed, why are we asked again? " )
101
-
102
- case . modifying:
103
- preconditionFailure ( " Invalid state " )
104
-
105
100
case . failure( let error) :
106
- self . upstreamState = . consumed( . failure( error) )
101
+ self . downstreamState = . consumed( . failure( error) )
107
102
return self . eventLoop. makeFailedFuture ( error)
108
103
}
109
104
}
110
105
106
+ // MARK: Consume on EventLoop
107
+
111
108
func onRow( _ onRow: @escaping ( PSQLRow ) throws -> ( ) ) -> EventLoopFuture < Void > {
112
109
if self . eventLoop. inEventLoop {
113
110
return self . onRow0 ( onRow)
@@ -121,7 +118,11 @@ final class PSQLRowStream {
121
118
private func onRow0( _ onRow: @escaping ( PSQLRow ) throws -> ( ) ) -> EventLoopFuture < Void > {
122
119
self . eventLoop. preconditionInEventLoop ( )
123
120
124
- switch self . upstreamState {
121
+ guard case . waitingForConsumer( let bufferState) = self . downstreamState else {
122
+ preconditionFailure ( " Invalid state: \( self . downstreamState) " )
123
+ }
124
+
125
+ switch bufferState {
125
126
case . streaming( var buffer, let dataSource) :
126
127
let promise = self . eventLoop. makePromise ( of: Void . self)
127
128
do {
@@ -136,12 +137,11 @@ final class PSQLRowStream {
136
137
}
137
138
138
139
buffer. removeAll ( )
139
- self . upstreamState = . streaming( buffer: buffer, dataSource: dataSource)
140
- self . downstreamState = . iteratingRows( onRow: onRow, promise)
140
+ self . downstreamState = . iteratingRows( onRow: onRow, promise, dataSource)
141
141
// immediately request more
142
142
dataSource. request ( for: self )
143
143
} catch {
144
- self . upstreamState = . failure( error)
144
+ self . downstreamState = . consumed ( . failure( error) )
145
145
dataSource. cancel ( for: self )
146
146
promise. fail ( error)
147
147
}
@@ -160,22 +160,15 @@ final class PSQLRowStream {
160
160
try onRow ( row)
161
161
}
162
162
163
- self . upstreamState = . consumed( . success( commandTag) )
164
- self . downstreamState = . consuming
163
+ self . downstreamState = . consumed( . success( commandTag) )
165
164
return self . eventLoop. makeSucceededVoidFuture ( )
166
165
} catch {
167
- self . upstreamState = . consumed( . failure( error) )
166
+ self . downstreamState = . consumed( . failure( error) )
168
167
return self . eventLoop. makeFailedFuture ( error)
169
168
}
170
169
171
- case . consumed:
172
- preconditionFailure ( " We already signaled, that the stream has completed, why are we asked again? " )
173
-
174
- case . modifying:
175
- preconditionFailure ( " Invalid state " )
176
-
177
170
case . failure( let error) :
178
- self . upstreamState = . consumed( . failure( error) )
171
+ self . downstreamState = . consumed( . failure( error) )
179
172
return self . eventLoop. makeFailedFuture ( error)
180
173
}
181
174
}
@@ -193,13 +186,15 @@ final class PSQLRowStream {
193
186
" row_count " : " \( newRows. count) "
194
187
] )
195
188
196
- guard case . streaming( var buffer, let dataSource) = self . upstreamState else {
197
- preconditionFailure ( " Invalid state " )
198
- }
199
-
200
189
switch self . downstreamState {
201
- case . iteratingRows( let onRow, let promise) :
202
- precondition ( buffer. isEmpty)
190
+ case . waitingForConsumer( . streaming( buffer: var buffer, dataSource: let dataSource) ) :
191
+ buffer. append ( contentsOf: newRows)
192
+ self . downstreamState = . waitingForConsumer( . streaming( buffer: buffer, dataSource: dataSource) )
193
+
194
+ case . waitingForConsumer( . finished) , . waitingForConsumer( . failure) :
195
+ preconditionFailure ( " How can new rows be received, if an end was already signalled? " )
196
+
197
+ case . iteratingRows( let onRow, let promise, let dataSource) :
203
198
do {
204
199
for data in newRows {
205
200
let row = PSQLRow (
@@ -214,82 +209,83 @@ final class PSQLRowStream {
214
209
dataSource. request ( for: self )
215
210
} catch {
216
211
dataSource. cancel ( for: self )
217
- self . upstreamState = . failure( error)
212
+ self . downstreamState = . consumed ( . failure( error) )
218
213
promise. fail ( error)
219
214
return
220
215
}
221
- case . waitingForAll:
222
- self . upstreamState = . modifying
223
- buffer. append ( contentsOf: newRows)
224
- self . upstreamState = . streaming( buffer: buffer, dataSource: dataSource)
225
-
216
+
217
+ case . waitingForAll( var rows, let promise, let dataSource) :
218
+ newRows. forEach { data in
219
+ let row = PSQLRow ( data: data, lookupTable: self . lookupTable, columns: self . rowDescription, jsonDecoder: self . jsonDecoder)
220
+ rows. append ( row)
221
+ }
222
+ self . downstreamState = . waitingForAll( rows, promise, dataSource)
226
223
// immediately request more
227
224
dataSource. request ( for: self )
228
225
229
- case . consuming:
230
- // this might happen, if the query has finished while the user is consuming data
231
- // we don't need to ask for more since the user is consuming anyway
232
- self . upstreamState = . modifying
233
- buffer. append ( contentsOf: newRows)
234
- self . upstreamState = . streaming( buffer: buffer, dataSource: dataSource)
226
+ case . consumed( . success) :
227
+ preconditionFailure ( " How can we receive further rows, if we are supposed to be done " )
228
+
229
+ case . consumed( . failure) :
230
+ break
235
231
}
236
232
}
237
233
238
234
internal func receive( completion result: Result < String , Error > ) {
239
235
self . eventLoop. preconditionInEventLoop ( )
240
236
241
- guard case . streaming( let oldBuffer, _) = self . upstreamState else {
242
- preconditionFailure ( " Invalid state " )
237
+ switch result {
238
+ case . success( let commandTag) :
239
+ self . receiveEnd ( commandTag)
240
+ case . failure( let error) :
241
+ self . receiveError ( error)
243
242
}
243
+ }
244
244
245
+ private func receiveEnd( _ commandTag: String ) {
245
246
switch self . downstreamState {
246
- case . iteratingRows( _, let promise) :
247
- precondition ( oldBuffer. isEmpty)
248
- self . downstreamState = . consuming
249
- self . upstreamState = . consumed( result)
250
- switch result {
251
- case . success:
252
- promise. succeed ( ( ) )
253
- case . failure( let error) :
254
- promise. fail ( error)
255
- }
247
+ case . waitingForConsumer( . streaming( buffer: let buffer, _) ) :
248
+ self . downstreamState = . waitingForConsumer( . finished( buffer: buffer, commandTag: commandTag) )
256
249
250
+ case . waitingForConsumer( . finished) , . waitingForConsumer( . failure) :
251
+ preconditionFailure ( " How can we get another end, if an end was already signalled? " )
257
252
258
- case . consuming:
259
- switch result {
260
- case . success( let commandTag) :
261
- self . upstreamState = . finished( buffer: oldBuffer, commandTag: commandTag)
262
- case . failure( let error) :
263
- self . upstreamState = . failure( error)
264
- }
265
-
266
- case . waitingForAll( let promise) :
267
- switch result {
268
- case . failure( let error) :
269
- self . upstreamState = . consumed( . failure( error) )
270
- promise. fail ( error)
271
- case . success( let commandTag) :
272
- let rows = oldBuffer. map {
273
- PSQLRow ( data: $0, lookupTable: self . lookupTable, columns: self . rowDescription, jsonDecoder: self . jsonDecoder)
274
- }
275
- self . upstreamState = . consumed( . success( commandTag) )
276
- promise. succeed ( rows)
277
- }
253
+ case . iteratingRows( _, let promise, _) :
254
+ self . downstreamState = . consumed( . success( commandTag) )
255
+ promise. succeed ( ( ) )
256
+
257
+ case . waitingForAll( let rows, let promise, _) :
258
+ self . downstreamState = . consumed( . success( commandTag) )
259
+ promise. succeed ( rows)
260
+
261
+ case . consumed:
262
+ break
278
263
}
279
264
}
280
-
281
- func cancel( ) {
282
- guard case . streaming( _, let dataSource) = self . upstreamState else {
283
- // We don't need to cancel any upstream resource. All needed data is already
284
- // included in this
285
- return
286
- }
287
265
288
- dataSource. cancel ( for: self )
266
+ private func receiveError( _ error: Error ) {
267
+ switch self . downstreamState {
268
+ case . waitingForConsumer( . streaming) :
269
+ self . downstreamState = . waitingForConsumer( . failure( error) )
270
+
271
+ case . waitingForConsumer( . finished) , . waitingForConsumer( . failure) :
272
+ preconditionFailure ( " How can we get another end, if an end was already signalled? " )
273
+
274
+ case . iteratingRows( _, let promise, _) :
275
+ self . downstreamState = . consumed( . failure( error) )
276
+ promise. fail ( error)
277
+
278
+ case . waitingForAll( _, let promise, _) :
279
+ self . downstreamState = . consumed( . failure( error) )
280
+ promise. fail ( error)
281
+
282
+ case . consumed:
283
+ break
284
+ }
289
285
}
290
286
291
287
var commandTag : String {
292
- guard case . consumed( . success( let commandTag) ) = self . upstreamState else {
288
+ guard case . consumed( . success( let commandTag) ) = self . downstreamState else {
293
289
preconditionFailure ( " commandTag may only be called if all rows have been consumed " )
294
290
}
295
291
return commandTag
0 commit comments