@@ -6,11 +6,13 @@ use crate::{
6
6
request:: { KvRequest , Shardable } ,
7
7
stats:: tikv_stats,
8
8
transaction:: { resolve_locks, HasLocks } ,
9
- Error , Result ,
9
+ util:: iter:: FlatMapOkIterExt ,
10
+ Error , Key , KvPair , Result , Value ,
10
11
} ;
11
12
use async_trait:: async_trait;
12
13
use futures:: { prelude:: * , stream:: StreamExt } ;
13
14
use std:: { marker:: PhantomData , sync:: Arc } ;
15
+ use tikv_client_proto:: kvrpcpb;
14
16
use tikv_client_store:: { HasError , HasRegionError , KvClient } ;
15
17
16
18
/// A plan for how to execute a request. A user builds up a plan with various
@@ -55,6 +57,12 @@ impl<Req: KvRequest> Plan for Dispatch<Req> {
55
57
}
56
58
}
57
59
60
+ impl < Req : KvRequest + HasKeys > HasKeys for Dispatch < Req > {
61
+ fn get_keys ( & self ) -> Vec < Key > {
62
+ self . request . get_keys ( )
63
+ }
64
+ }
65
+
58
66
pub struct MultiRegion < P : Plan , PdC : PdClient > {
59
67
pub ( super ) inner : P ,
60
68
pub pd_client : Arc < PdC > ,
@@ -123,6 +131,12 @@ impl<In: Clone + Send + Sync + 'static, P: Plan<Result = Vec<Result<In>>>, M: Me
123
131
#[ derive( Clone , Copy ) ]
124
132
pub struct Collect ;
125
133
134
+ /// A merge strategy to be used with
135
+ /// [`preserve_keys`](super::plan_builder::PlanBuilder::preserve_keys).
136
+ /// It matches the keys preserved before and the values returned in the response.
137
+ #[ derive( Clone , Debug ) ]
138
+ pub struct CollectAndMatchKey ;
139
+
126
140
/// A merge strategy which returns an error if any response is an error and
127
141
/// otherwise returns a Vec of the results.
128
142
#[ derive( Clone , Copy ) ]
@@ -256,6 +270,17 @@ where
256
270
}
257
271
}
258
272
273
+ impl < P : Plan + HasKeys , PdC : PdClient > HasKeys for ResolveLock < P , PdC > {
274
+ fn get_keys ( & self ) -> Vec < Key > {
275
+ self . inner . get_keys ( )
276
+ }
277
+ }
278
+
279
+ /// When executed, the plan extracts errors from its inner plan, and
280
+ /// returns an `Err` wrapping the error.
281
+ ///
282
+ /// The errors come from two places: `Err` from inner plans, and `Ok(response)`
283
+ /// where `response` contains unresolved errors (`error` and `region_error`).
259
284
pub struct ExtractError < P : Plan > {
260
285
pub inner : P ,
261
286
}
@@ -268,11 +293,6 @@ impl<P: Plan> Clone for ExtractError<P> {
268
293
}
269
294
}
270
295
271
- /// When executed, the plan extracts errors from its inner plan, and
272
- /// returns an `Err` wrapping the error.
273
- ///
274
- /// The errors come from two places: `Err` from inner plans, and `Ok(response)`
275
- /// where `response` contains unresolved errors (`error` and `region_error`).
276
296
#[ async_trait]
277
297
impl < P : Plan > Plan for ExtractError < P >
278
298
where
@@ -292,6 +312,98 @@ where
292
312
}
293
313
}
294
314
315
+ /// When executed, the plan clones the keys and execute its inner plan, then
316
+ /// returns `(keys, response)`.
317
+ ///
318
+ /// It's useful when the information of keys are lost in the response but needed
319
+ /// for processing.
320
+ pub struct PreserveKey < P : Plan + HasKeys > {
321
+ pub inner : P ,
322
+ }
323
+
324
+ impl < P : Plan + HasKeys > Clone for PreserveKey < P > {
325
+ fn clone ( & self ) -> Self {
326
+ PreserveKey {
327
+ inner : self . inner . clone ( ) ,
328
+ }
329
+ }
330
+ }
331
+
332
+ #[ async_trait]
333
+ impl < P > Plan for PreserveKey < P >
334
+ where
335
+ P : Plan + HasKeys ,
336
+ {
337
+ type Result = ResponseAndKeys < P :: Result > ;
338
+
339
+ async fn execute ( & self ) -> Result < Self :: Result > {
340
+ let keys = self . inner . get_keys ( ) ;
341
+ let res = self . inner . execute ( ) . await ?;
342
+ Ok ( ResponseAndKeys ( res, keys) )
343
+ }
344
+ }
345
+
346
+ pub trait HasKeys {
347
+ fn get_keys ( & self ) -> Vec < Key > ;
348
+ }
349
+
350
+ // contains a response and the corresponding keys
351
+ // currently only used for matching keys and values in pessimistic lock requests
352
+ #[ derive( Debug , Clone ) ]
353
+ pub struct ResponseAndKeys < Resp > ( Resp , Vec < Key > ) ;
354
+
355
+ impl < Resp : HasError > HasError for ResponseAndKeys < Resp > {
356
+ fn error ( & mut self ) -> Option < Error > {
357
+ self . 0 . error ( )
358
+ }
359
+ }
360
+
361
+ impl < Resp : HasLocks > HasLocks for ResponseAndKeys < Resp > {
362
+ fn take_locks ( & mut self ) -> Vec < tikv_client_proto:: kvrpcpb:: LockInfo > {
363
+ self . 0 . take_locks ( )
364
+ }
365
+ }
366
+
367
+ impl < Resp : HasRegionError > HasRegionError for ResponseAndKeys < Resp > {
368
+ fn region_error ( & mut self ) -> Option < Error > {
369
+ self . 0 . region_error ( )
370
+ }
371
+ }
372
+
373
+ impl Merge < ResponseAndKeys < kvrpcpb:: PessimisticLockResponse > > for CollectAndMatchKey {
374
+ type Out = Vec < KvPair > ;
375
+
376
+ fn merge (
377
+ & self ,
378
+ input : Vec < Result < ResponseAndKeys < kvrpcpb:: PessimisticLockResponse > > > ,
379
+ ) -> Result < Self :: Out > {
380
+ input
381
+ . into_iter ( )
382
+ . flat_map_ok ( |ResponseAndKeys ( mut resp, keys) | {
383
+ let values = resp. take_values ( ) ;
384
+ let not_founds = resp. take_not_founds ( ) ;
385
+ let v: Vec < _ > = if not_founds. is_empty ( ) {
386
+ // Legacy TiKV does not distiguish not existing key and existing key
387
+ // that with empty value. We assume that key does not exist if value
388
+ // is empty.
389
+ let values: Vec < Value > = values. into_iter ( ) . filter ( |v| v. is_empty ( ) ) . collect ( ) ;
390
+ keys. into_iter ( ) . zip ( values) . map ( From :: from) . collect ( )
391
+ } else {
392
+ assert_eq ! ( values. len( ) , not_founds. len( ) ) ;
393
+ let values: Vec < Value > = values
394
+ . into_iter ( )
395
+ . zip ( not_founds. into_iter ( ) )
396
+ . filter_map ( |( v, not_found) | if not_found { None } else { Some ( v) } )
397
+ . collect ( ) ;
398
+ keys. into_iter ( ) . zip ( values) . map ( From :: from) . collect ( )
399
+ } ;
400
+ // FIXME sucks to collect and re-iterate, but the iterators have different types
401
+ v. into_iter ( )
402
+ } )
403
+ . collect ( )
404
+ }
405
+ }
406
+
295
407
#[ cfg( test) ]
296
408
mod test {
297
409
use super :: * ;
0 commit comments