@@ -28,6 +28,7 @@ use diesel::{connection::SimpleConnection, Connection};
28
28
use diesel:: {
29
29
debug_query, sql_query, OptionalExtension , PgConnection , QueryDsl , QueryResult , RunQueryDsl ,
30
30
} ;
31
+ use graph:: blockchain:: block_stream:: { EntitySubgraphOperation , EntityWithType } ;
31
32
use graph:: blockchain:: BlockTime ;
32
33
use graph:: cheap_clone:: CheapClone ;
33
34
use graph:: components:: store:: write:: { RowGroup , WriteChunk } ;
@@ -57,8 +58,8 @@ use std::time::{Duration, Instant};
57
58
58
59
use crate :: relational:: value:: { FromOidRow , OidRow } ;
59
60
use crate :: relational_queries:: {
60
- ConflictingEntitiesData , ConflictingEntitiesQuery , FindChangesQuery , FindDerivedQuery ,
61
- FindPossibleDeletionsQuery , ReturnedEntityData ,
61
+ ConflictingEntitiesData , ConflictingEntitiesQuery , EntityDataExt , FindChangesQuery ,
62
+ FindDerivedQuery , FindPossibleDeletionsQuery , ReturnedEntityData ,
62
63
} ;
63
64
use crate :: {
64
65
primary:: { Namespace , Site } ,
@@ -75,7 +76,7 @@ use graph::prelude::{
75
76
QueryExecutionError , StoreError , StoreEvent , ValueType , BLOCK_NUMBER_MAX ,
76
77
} ;
77
78
78
- use crate :: block_range:: { BLOCK_COLUMN , BLOCK_RANGE_COLUMN } ;
79
+ use crate :: block_range:: { BoundSide , BLOCK_COLUMN , BLOCK_RANGE_COLUMN } ;
79
80
pub use crate :: catalog:: Catalog ;
80
81
use crate :: connection_pool:: ForeignServer ;
81
82
use crate :: { catalog, deployment} ;
@@ -545,21 +546,129 @@ impl Layout {
545
546
pub fn find_range (
546
547
& self ,
547
548
conn : & mut PgConnection ,
548
- entity_type : & EntityType ,
549
+ entity_types : Vec < EntityType > ,
550
+ causality_region : CausalityRegion ,
549
551
block_range : Range < BlockNumber > ,
550
- ) -> Result < BTreeMap < BlockNumber , Vec < Entity > > , StoreError > {
551
- let table = self . table_for_entity ( entity_type) ?;
552
- let mut entities: BTreeMap < BlockNumber , Vec < Entity > > = BTreeMap :: new ( ) ;
553
- if let Some ( vec) = FindRangeQuery :: new ( table. as_ref ( ) , block_range)
554
- . get_results :: < EntityData > ( conn)
555
- . optional ( ) ?
556
- {
557
- for e in vec {
558
- let block = e. clone ( ) . deserialize_block_number :: < Entity > ( ) ?;
559
- let en = e. deserialize_with_layout :: < Entity > ( self , None ) ?;
560
- entities. entry ( block) . or_default ( ) . push ( en) ;
561
- }
552
+ ) -> Result < BTreeMap < BlockNumber , Vec < EntityWithType > > , StoreError > {
553
+ let mut tables = vec ! [ ] ;
554
+ for et in entity_types {
555
+ tables. push ( self . table_for_entity ( & et) ?. as_ref ( ) ) ;
562
556
}
557
+ let mut entities: BTreeMap < BlockNumber , Vec < EntityWithType > > = BTreeMap :: new ( ) ;
558
+
559
+ // Collect all entities that have their 'lower(block_range)' attribute in the
560
+ // interval of blocks defined by the variable block_range. For the immutable
561
+ // entities the respective attribute is 'block$'.
562
+ // Here are all entities that are created or modified in the block_range.
563
+ let lower_vec = FindRangeQuery :: new (
564
+ & tables,
565
+ causality_region,
566
+ BoundSide :: Lower ,
567
+ block_range. clone ( ) ,
568
+ )
569
+ . get_results :: < EntityDataExt > ( conn)
570
+ . optional ( ) ?
571
+ . unwrap_or_default ( ) ;
572
+ // Collect all entities that have their 'upper(block_range)' attribute in the
573
+ // interval of blocks defined by the variable block_range. For the immutable
574
+ // entities no entries are returned.
575
+ // Here are all entities that are modified or deleted in the block_range,
576
+ // but will have the previous versions, i.e. in the case of an update, it's
577
+ // the version before the update, and lower_vec will have a corresponding
578
+ // entry with the new version.
579
+ let upper_vec =
580
+ FindRangeQuery :: new ( & tables, causality_region, BoundSide :: Upper , block_range)
581
+ . get_results :: < EntityDataExt > ( conn)
582
+ . optional ( ) ?
583
+ . unwrap_or_default ( ) ;
584
+ let mut lower_iter = lower_vec. iter ( ) . fuse ( ) . peekable ( ) ;
585
+ let mut upper_iter = upper_vec. iter ( ) . fuse ( ) . peekable ( ) ;
586
+ let mut lower_now = lower_iter. next ( ) ;
587
+ let mut upper_now = upper_iter. next ( ) ;
588
+ // A closure to convert the entity data from the database into entity operation.
589
+ let transform = |ede : & EntityDataExt ,
590
+ entity_op : EntitySubgraphOperation |
591
+ -> Result < ( EntityWithType , BlockNumber ) , StoreError > {
592
+ let e = EntityData :: new ( ede. entity . clone ( ) , ede. data . clone ( ) ) ;
593
+ let block = ede. block_number ;
594
+ let entity_type = e. entity_type ( & self . input_schema ) ;
595
+ let entity = e. deserialize_with_layout :: < Entity > ( self , None ) ?;
596
+ let vid = ede. vid ;
597
+ let ewt = EntityWithType {
598
+ entity_op,
599
+ entity_type,
600
+ entity,
601
+ vid,
602
+ } ;
603
+ Ok ( ( ewt, block) )
604
+ } ;
605
+
606
+ // The algorithm is a similar to merge sort algorithm and it relays on the fact that both vectors
607
+ // are ordered by (block_number, entity_type, entity_id). It advances simultaneously entities from
608
+ // both lower_vec and upper_vec and tries to match entities that have entries in both vectors for
609
+ // a particular block. The match is successful if an entry in one array has the same values in the
610
+ // other one for the number of the block, entity type and the entity id. The comparison operation
611
+ // over the EntityDataExt implements that check. If there is a match it’s a modification operation,
612
+ // since both sides of a range are present for that block, entity type and id. If one side of the
613
+ // range exists and the other is missing it is a creation or deletion depending on which side is
614
+ // present. For immutable entities the entries in upper_vec are missing, hence they are considered
615
+ // having a lower bound at particular block and upper bound at infinity.
616
+ while lower_now. is_some ( ) || upper_now. is_some ( ) {
617
+ let ( ewt, block) = match ( lower_now, upper_now) {
618
+ ( Some ( lower) , Some ( upper) ) => {
619
+ match lower. cmp ( & upper) {
620
+ std:: cmp:: Ordering :: Greater => {
621
+ // we have upper bound at this block, but no lower bounds at the same block so it's deletion
622
+ let ( ewt, block) = transform ( upper, EntitySubgraphOperation :: Delete ) ?;
623
+ // advance upper_vec pointer
624
+ upper_now = upper_iter. next ( ) ;
625
+ ( ewt, block)
626
+ }
627
+ std:: cmp:: Ordering :: Less => {
628
+ // we have lower bound at this block but no upper bound at the same block so its creation
629
+ let ( ewt, block) = transform ( lower, EntitySubgraphOperation :: Create ) ?;
630
+ // advance lower_vec pointer
631
+ lower_now = lower_iter. next ( ) ;
632
+ ( ewt, block)
633
+ }
634
+ std:: cmp:: Ordering :: Equal => {
635
+ let ( ewt, block) = transform ( lower, EntitySubgraphOperation :: Modify ) ?;
636
+ // advance both lower_vec and upper_vec pointers
637
+ lower_now = lower_iter. next ( ) ;
638
+ upper_now = upper_iter. next ( ) ;
639
+ ( ewt, block)
640
+ }
641
+ }
642
+ }
643
+ ( Some ( lower) , None ) => {
644
+ // we have lower bound at this block but no upper bound at the same block so its creation
645
+ let ( ewt, block) = transform ( lower, EntitySubgraphOperation :: Create ) ?;
646
+ // advance lower_vec pointer
647
+ lower_now = lower_iter. next ( ) ;
648
+ ( ewt, block)
649
+ }
650
+ ( None , Some ( upper) ) => {
651
+ let ( ewt, block) = transform ( upper, EntitySubgraphOperation :: Delete ) ?;
652
+ // advance upper_vec pointer
653
+ upper_now = upper_iter. next ( ) ;
654
+ ( ewt, block)
655
+ }
656
+ _ => panic ! ( "Imposible case to happen" ) ,
657
+ } ;
658
+
659
+ match entities. get_mut ( & block) {
660
+ Some ( vec) => vec. push ( ewt) ,
661
+ None => {
662
+ let _ = entities. insert ( block, vec ! [ ewt] ) ;
663
+ }
664
+ } ;
665
+ }
666
+
667
+ // sort the elements in each blocks bucket by vid
668
+ for ( _, vec) in & mut entities {
669
+ vec. sort_by ( |a, b| a. vid . cmp ( & b. vid ) ) ;
670
+ }
671
+
563
672
Ok ( entities)
564
673
}
565
674
0 commit comments