Skip to content

Commit d1681a7

Browse files
authored
feat(query): refresh TopN statistics on append (#20091)
* feat(query): refresh topn statistics on append * refactor(query): simplify append topn stats commit * fix: add missing topn capacity in selectivity tests * fix(storage): drop stale CMS on append TopN refresh * fix(storage): don't seed TopN from missing stats * fix: drop stale histograms on append topn refresh * fix(storage): refresh topn for added nullable columns * feat(fuse): cache TopN statistics per segment * revert: cache TopN statistics per segment * feat(fuse): cache per-block TopN in segment statistics * fix(fuse): address statistics review feedback * fix(fuse): refresh stats metadata for TopN-only appends
1 parent a6bb187 commit d1681a7

41 files changed

Lines changed: 1591 additions & 312 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/common/frozen_api/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ fn is_custom_type(name: &str) -> bool {
234234
| "ClusterKey"
235235
| "StatisticsOfColumns"
236236
| "BlockHLL"
237+
| "BlockTopN"
237238
| "SnapshotId"
238239
)
239240
}

src/query/expression/src/values.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ use crate::with_opaque_size;
116116
use crate::with_opaque_size_mapped;
117117
use crate::with_opaque_type;
118118

119+
pub const LARGE_STRING_BYTES_THRESHOLD: usize = 256;
120+
119121
#[derive(Debug, Clone, PartialEq, EnumAsInner)]
120122
pub enum Value<T: AccessType> {
121123
Scalar(T::Scalar),
@@ -1913,7 +1915,7 @@ impl Column {
19131915
}
19141916
}
19151917

1916-
/// Checks if the average length of a string column exceeds 256 bytes.
1918+
/// Checks if the average length of a string column exceeds LARGE_STRING_BYTES_THRESHOLD bytes.
19171919
/// If it does, the bloom index for the column will not be established.
19181920
pub fn check_large_string(&self) -> bool {
19191921
let (inner, len) = if let Column::Nullable(c) = self {
@@ -1923,7 +1925,7 @@ impl Column {
19231925
};
19241926
if let Column::String(v) = inner {
19251927
let bytes_per_row = v.total_bytes_len() / len.max(1);
1926-
if bytes_per_row > 256 {
1928+
if bytes_per_row > LARGE_STRING_BYTES_THRESHOLD {
19271929
return true;
19281930
}
19291931
}

src/query/service/src/interpreters/interpreter_table_drop_column.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use databend_common_sql::binder::validate_table_indexes_not_referencing_columns;
2626
use databend_common_sql::plans::DropTableColumnPlan;
2727
use databend_common_storages_basic::view_table::VIEW_ENGINE;
2828
use databend_common_storages_stream::stream_table::STREAM_ENGINE;
29+
use databend_storages_common_table_meta::table::OPT_KEY_ANALYZE_FREQUENCY_COLUMNS;
2930
use databend_storages_common_table_meta::table::OPT_KEY_APPROX_DISTINCT_COLUMNS;
3031
use databend_storages_common_table_meta::table::OPT_KEY_BLOOM_INDEX_COLUMNS;
3132

@@ -159,6 +160,16 @@ impl Interpreter for DropTableColumnInterpreter {
159160
}
160161
}
161162
}
163+
if let Some(value) = opts.get_mut(OPT_KEY_ANALYZE_FREQUENCY_COLUMNS) {
164+
if let ApproxDistinctColumns::Specify(mut cols) =
165+
value.parse::<ApproxDistinctColumns>()?
166+
{
167+
if let Some(pos) = cols.iter().position(|x| *x == self.plan.column) {
168+
cols.remove(pos);
169+
*value = cols.join(",");
170+
}
171+
}
172+
}
162173
let new_schema = new_table_meta.schema.as_ref().clone();
163174

164175
let dropped_column_ids = field.column_ids().into_iter().collect();

src/query/service/src/interpreters/interpreter_table_modify_column.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use databend_storages_common_index::RangeIndex;
6161
use databend_storages_common_table_meta::meta::SnapshotId;
6262
use databend_storages_common_table_meta::meta::TableMetaTimestamps;
6363
use databend_storages_common_table_meta::readers::snapshot_reader::TableSnapshotAccessor;
64+
use databend_storages_common_table_meta::table::OPT_KEY_ANALYZE_FREQUENCY_COLUMNS;
6465
use databend_storages_common_table_meta::table::OPT_KEY_APPROX_DISTINCT_COLUMNS;
6566
use databend_storages_common_table_meta::table::OPT_KEY_BLOOM_INDEX_COLUMNS;
6667

@@ -308,6 +309,12 @@ impl ModifyTableColumnInterpreter {
308309
approx_distinct_cols = cols;
309310
}
310311
}
312+
let mut analyze_frequency_cols = vec![];
313+
if let Some(v) = table_info.options().get(OPT_KEY_ANALYZE_FREQUENCY_COLUMNS) {
314+
if let ApproxDistinctColumns::Specify(cols) = v.parse::<ApproxDistinctColumns>()? {
315+
analyze_frequency_cols = cols;
316+
}
317+
}
311318

312319
let mut table_info = table.get_table_info().clone();
313320
table_info.meta.fill_field_comments();
@@ -345,6 +352,16 @@ impl ModifyTableColumnInterpreter {
345352
field.data_type
346353
)));
347354
}
355+
if analyze_frequency_cols
356+
.iter()
357+
.any(|v| v.as_str() == field.name)
358+
&& !RangeIndex::supported_table_type(&field.data_type)
359+
{
360+
return Err(ErrorCode::TableOptionInvalid(format!(
361+
"Unsupported data type '{}' for analyze frequency columns",
362+
field.data_type
363+
)));
364+
}
348365
// If the column is inverted index column, the type can't be changed.
349366
if !table_info.meta.indexes.is_empty() {
350367
for (index_name, index) in &table_info.meta.indexes {

src/query/service/src/interpreters/interpreter_table_rename_column.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use databend_common_sql::plans::RenameTableColumnPlan;
2323
use databend_common_storages_basic::view_table::VIEW_ENGINE;
2424
use databend_common_storages_iceberg::table::ICEBERG_ENGINE;
2525
use databend_common_storages_stream::stream_table::STREAM_ENGINE;
26+
use databend_storages_common_table_meta::table::OPT_KEY_ANALYZE_FREQUENCY_COLUMNS;
2627
use databend_storages_common_table_meta::table::OPT_KEY_APPROX_DISTINCT_COLUMNS;
2728
use databend_storages_common_table_meta::table::OPT_KEY_BLOOM_INDEX_COLUMNS;
2829

@@ -129,6 +130,14 @@ impl Interpreter for RenameTableColumnInterpreter {
129130
&self.plan.new_column,
130131
)?;
131132
}
133+
if let Some(value) = opts.get_mut(OPT_KEY_ANALYZE_FREQUENCY_COLUMNS) {
134+
rename_column_in_comma_separated_ident(
135+
self.ctx.as_ref(),
136+
value,
137+
&self.plan.old_column,
138+
&self.plan.new_column,
139+
)?;
140+
}
132141

133142
let mut new_cluster_key = None;
134143
if let Some((_, cluster_key)) = table.cluster_key_meta() {

src/query/service/src/physical_plans/physical_commit_sink.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ impl IPhysicalPlan for CommitSink {
179179
block_meta: Arc::unwrap_or_clone(block_meta),
180180
draft_virtual_block_meta: None,
181181
column_hlls: column_hlls.map(BlockHLLState::Serialized),
182+
column_top_n: None,
182183
})
183184
})
184185
.collect::<Vec<Arc<ExtendedBlockMeta>>>();

src/query/service/src/physical_plans/physical_mutation_source.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ impl IPhysicalPlan for MutationSource {
145145
virtual_schema: None,
146146
virtual_schema_mode: VirtualSchemaMode::Merge,
147147
hll: HashMap::new(),
148+
top_n: HashMap::new(),
148149
};
149150
let block = DataBlock::empty_with_meta(Box::new(meta));
150151
OneBlockSource::create(output, block)

src/query/service/src/test_kits/fuse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ async fn generate_blocks(
220220
block_metas.push(Arc::new(block_meta));
221221
hlls.push(hll);
222222
}
223-
let stats = SegmentStatistics::new(hlls).to_bytes()?;
223+
let stats = SegmentStatistics::new(hlls, Vec::new()).to_bytes()?;
224224
Ok((block_metas, stats))
225225
}
226226

src/query/sql/src/planner/plans/scan.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ mod tests {
564564
fn test_sampled_scan_clears_top_n_stats() -> Result<()> {
565565
let column = Symbol::new(1);
566566
let top_n = ColumnTopN {
567+
capacity: 1,
567568
values: vec![ColumnTopNEntry {
568569
scalar: Scalar::Number(NumberScalar::UInt64(42)),
569570
count: 37,

src/query/sql/tests/it/optimizer/collect_statistics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ async fn test_collect_statistics_skips_top_n_for_change_scan() -> Result<()> {
4141
ctx.configure_for_optimizer_case(true)?;
4242

4343
let top_n = ColumnTopN {
44+
capacity: 1,
4445
values: vec![ColumnTopNEntry {
4546
scalar: Scalar::Number(NumberScalar::UInt64(1)),
4647
count: 90,

0 commit comments

Comments
 (0)