@@ -522,21 +522,13 @@ impl FuseTable {
522522 insert_hll : & BlockHLL ,
523523 insert_rows : u64 ,
524524 insert_top_n : & BlockTopN ,
525- reuse_previous_stats : bool ,
526525 refresh_top_n : bool ,
527526 ) -> Result < TableStatsGenerator > {
528- let empty_snapshot = None ;
529- let stats_snapshot = if reuse_previous_stats {
530- snapshot
531- } else {
532- & empty_snapshot
533- } ;
534- // Extract reusable base stats meta (row_count / hll, etc.) from snapshot.
535- // Overwrite commits build statistics from inserted rows only.
536- let summary = stats_snapshot. summary ( ) ;
527+ // Extract previous stats meta (row_count / hll, etc.) from snapshot.
528+ let summary = snapshot. summary ( ) ;
537529 let prev_stats_meta = summary. additional_stats_meta . as_ref ( ) ;
538- // Reusable table statistics file location (if any).
539- let mut prev_stats_location = stats_snapshot . table_statistics_location ( ) ;
530+ // Previous statistics file location (if any).
531+ let mut prev_stats_location = snapshot . table_statistics_location ( ) ;
540532 let top_n_column_ids = if refresh_top_n && insert_rows > 0 {
541533 self . append_top_n_columns ( self . schema ( ) ) ?
542534 . map ( |( columns, _) | columns. values ( ) . map ( |field| field. column_id ( ) ) . collect ( ) )
@@ -559,43 +551,23 @@ impl FuseTable {
559551 ) ) ;
560552 }
561553
562- if insert_hll. is_empty ( ) {
563- let empty_hll = HashMap :: new ( ) ;
564- let table_statistics = self
565- . build_append_top_n_statistics (
566- stats_snapshot,
567- top_n_column_ids,
568- & empty_hll,
569- insert_top_n,
570- table_row_count,
571- )
572- . await ?;
573- if let Some ( stats) = & table_statistics {
574- prev_stats_location = Some ( self . new_table_statistics_location ( stats) ?) ;
575- }
576-
577- return Ok ( TableStatsGenerator :: new (
578- prev_stats_meta. cloned ( ) ,
579- prev_stats_location,
580- 0 ,
581- 0 ,
582- HashMap :: new ( ) ,
583- table_statistics,
584- ) ) ;
585- }
586-
587- // Initialize a new HLL with inserted rows
588554 let mut new_hll = insert_hll. clone ( ) ;
555+ let next_stats_meta = if insert_hll. is_empty ( ) {
556+ prev_stats_meta. cloned ( )
557+ } else {
558+ None
559+ } ;
589560 // Calculate updated row_count
590- let ( hll_row_count, unstats_rows) = match prev_stats_meta {
561+ let ( hll_row_count, unstats_rows) = match ( !insert_hll. is_empty ( ) , prev_stats_meta) {
562+ ( false , _) => ( 0 , 0 ) ,
591563 // Case 1: Previous stats exist and already contain HLL → merge directly
592- Some ( v) if v. hll . is_some ( ) => {
564+ ( true , Some ( v) ) if v. hll . is_some ( ) => {
593565 let prev_hll = decode_column_hll ( v. hll . as_ref ( ) . unwrap ( ) ) ?. unwrap ( ) ;
594566 merge_column_hll_mut ( & mut new_hll, & prev_hll) ;
595567 ( v. row_count + insert_rows, v. unstats_rows )
596568 }
597569 // Case 2: Previous meta has no HLL → need to load from stats file
598- _ => {
570+ ( true , _ ) => {
599571 if let Some ( loc) = & prev_stats_location {
600572 let ver = TableMetaLocationGenerator :: table_statistics_version ( loc) ;
601573 let reader = MetaReaders :: table_snapshot_statistics_reader ( self . get_operator ( ) ) ;
@@ -653,7 +625,7 @@ impl FuseTable {
653625
654626 let table_statistics = if has_top_n_update {
655627 self . build_append_top_n_statistics (
656- stats_snapshot ,
628+ snapshot ,
657629 top_n_column_ids,
658630 & new_hll,
659631 insert_top_n,
@@ -668,7 +640,7 @@ impl FuseTable {
668640 }
669641
670642 Ok ( TableStatsGenerator :: new (
671- None ,
643+ next_stats_meta ,
672644 prev_stats_location,
673645 hll_row_count,
674646 unstats_rows,
@@ -701,28 +673,18 @@ impl FuseTable {
701673 } else {
702674 None
703675 } ;
704- let empty_top_n = HashMap :: new ( ) ;
705- let ( mut top_n, append_top_n) = if let Some ( ( previous, prev_stats) ) = fresh_prev_stats {
706- let prev_top_n = ( previous. summary . row_count != 0 ) . then_some ( PreviousTopN {
707- top_n : & prev_stats. top_n ,
708- summary : & previous. summary ,
709- } ) ;
710- build_append_top_n_merge_inputs ( top_n_column_ids, prev_top_n, insert_top_n)
711- } else if let Some ( previous) = snapshot. as_ref ( )
712- && previous. summary . row_count != 0
713- {
714- build_append_top_n_merge_inputs (
715- top_n_column_ids,
716- Some ( PreviousTopN {
717- top_n : & empty_top_n,
718- summary : & previous. summary ,
719- } ) ,
720- insert_top_n,
721- )
722- } else {
723- build_append_top_n_merge_inputs ( top_n_column_ids, None , insert_top_n)
724- } ;
725676
677+ let mut top_n = fresh_prev_stats
678+ . map ( |( _, stats) | stats. top_n . clone ( ) )
679+ . unwrap_or_default ( ) ;
680+ top_n. retain ( |column_id, _| top_n_column_ids. contains ( column_id) ) ;
681+
682+ let append_top_n = append_top_n_merge_input (
683+ & top_n_column_ids,
684+ snapshot. as_ref ( ) . map ( |snapshot| & snapshot. summary ) ,
685+ & top_n,
686+ insert_top_n,
687+ ) ;
726688 merge_column_top_n_mut ( & mut top_n, append_top_n) ?;
727689 if top_n. is_empty ( ) {
728690 return Ok ( None ) ;
@@ -753,58 +715,40 @@ impl FuseTable {
753715 }
754716}
755717
756- #[ derive( Clone , Copy ) ]
757- struct PreviousTopN < ' a > {
758- top_n : & ' a BlockTopN ,
759- summary : & ' a Statistics ,
760- }
761-
762- impl PreviousTopN < ' _ > {
763- fn missing_error ( & self , column_id : ColumnId ) -> Option < u64 > {
764- self . summary
765- . col_stats
766- . get ( & column_id)
767- . map ( |stats| self . summary . row_count . saturating_sub ( stats. null_count ) )
768- }
769- }
770-
771- fn build_append_top_n_merge_inputs (
772- top_n_column_ids : Vec < ColumnId > ,
773- prev_top_n : Option < PreviousTopN < ' _ > > ,
718+ fn append_top_n_merge_input (
719+ column_ids : & [ ColumnId ] ,
720+ previous_summary : Option < & Statistics > ,
721+ previous_top_n : & BlockTopN ,
774722 insert_top_n : & BlockTopN ,
775- ) -> ( BlockTopN , BlockTopN ) {
776- let mut top_n = HashMap :: new ( ) ;
723+ ) -> BlockTopN {
724+ let previous_summary = previous_summary . filter ( |summary| summary . row_count != 0 ) ;
777725 let mut append_top_n = HashMap :: new ( ) ;
778726
779- for column_id in top_n_column_ids {
780- match prev_top_n {
781- Some ( prev_top_n) => {
782- let append_column_top_n = insert_top_n. get ( & column_id) . cloned ( ) ;
783- if let Some ( column_top_n) = prev_top_n. top_n . get ( & column_id) . cloned ( ) {
784- top_n. insert ( column_id, column_top_n) ;
785- if let Some ( column_top_n) = append_column_top_n {
786- append_top_n. insert ( column_id, column_top_n) ;
787- }
788- } else if let Some ( mut column_top_n) = append_column_top_n {
789- let Some ( missing_error) = prev_top_n. missing_error ( column_id) else {
790- continue ;
791- } ;
792- for entry in & mut column_top_n. values {
793- entry. count = entry. count . saturating_add ( missing_error) ;
794- entry. error = entry. error . saturating_add ( missing_error) ;
795- }
796- append_top_n. insert ( column_id, column_top_n) ;
797- }
798- }
799- None => {
800- if let Some ( column_top_n) = insert_top_n. get ( & column_id) . cloned ( ) {
801- append_top_n. insert ( column_id, column_top_n) ;
802- }
803- }
727+ for & column_id in column_ids {
728+ let Some ( column_top_n) = insert_top_n. get ( & column_id) . cloned ( ) else {
729+ continue ;
730+ } ;
731+ if previous_top_n. contains_key ( & column_id)
732+ || column_has_no_previous_values ( previous_summary, column_id)
733+ {
734+ append_top_n. insert ( column_id, column_top_n) ;
804735 }
805736 }
806737
807- ( top_n, append_top_n)
738+ append_top_n
739+ }
740+
741+ fn column_has_no_previous_values (
742+ previous_summary : Option < & Statistics > ,
743+ column_id : ColumnId ,
744+ ) -> bool {
745+ let Some ( summary) = previous_summary else {
746+ return true ;
747+ } ;
748+ summary
749+ . col_stats
750+ . get ( & column_id)
751+ . is_none_or ( |stats| stats. null_count == summary. row_count )
808752}
809753
810754pub ( crate ) fn is_fresh_table_snapshot_top_n (
0 commit comments