@@ -68,11 +68,19 @@ pub fn gen_columns_statistics(
6868 let ( distinct_of_values, unset_bits) =
6969 if s == Scalar :: Null { ( 0 , rows) } else { ( 1 , 0 ) } ;
7070
71+ let Some ( ( min, max) ) = trim_column_min_max (
72+ s. clone ( ) ,
73+ s. clone ( ) ,
74+ col_stats_truncate_lens. get ( & column_id) . copied ( ) ,
75+ ) else {
76+ continue ;
77+ } ;
78+
7179 // when we read it back from parquet, it is a Column instead of Scalar
7280 let in_memory_size = s. as_ref ( ) . estimated_scalar_repeat_size ( rows, & data_type) ;
7381 let col_stats = ColumnStatistics :: new (
74- s . clone ( ) ,
75- s . clone ( ) ,
82+ min ,
83+ max ,
7684 unset_bits as u64 ,
7785 in_memory_size as u64 ,
7886 Some ( distinct_of_values) ,
@@ -82,56 +90,27 @@ pub fn gen_columns_statistics(
8290 }
8391 Value :: Column ( col) => {
8492 // later, during the evaluation of expressions, name of field does not matter
85- let mut min = Scalar :: Null ;
86- let mut max = Scalar :: Null ;
87-
88- if col. len ( ) > 0 {
93+ let ( min, max) = if col. len ( ) > 0 {
8994 let ( mins, _) = eval_aggr ( "min" , vec ! [ ] , & [ col. clone ( ) . into ( ) ] , rows, vec ! [ ] ) ?;
9095 let ( maxs, _) = eval_aggr ( "max" , vec ! [ ] , & [ col. clone ( ) . into ( ) ] , rows, vec ! [ ] ) ?;
9196
92- let truncate_len = col_stats_truncate_lens
93- . get ( & column_id)
94- . copied ( )
95- . unwrap_or ( STATS_STRING_PREFIX_LEN ) ;
96-
97- if mins. len ( ) > 0 {
98- min = if let Some ( v) = mins. index ( 0 ) {
99- let owned = v. to_owned ( ) ;
100- let trimmed = match owned {
101- Scalar :: String ( s) => {
102- trim_string_min_with_len ( s, truncate_len) . map ( Scalar :: String )
103- }
104- other => other. trim_min ( ) ,
105- } ;
106- if let Some ( v) = trimmed {
107- v
108- } else {
109- continue ;
110- }
111- } else {
112- continue ;
113- }
114- }
115-
116- if maxs. len ( ) > 0 {
117- max = if let Some ( v) = maxs. index ( 0 ) {
118- let owned = v. to_owned ( ) ;
119- let trimmed = match owned {
120- Scalar :: String ( s) => {
121- trim_string_max_with_len ( s, truncate_len) . map ( Scalar :: String )
122- }
123- other => other. trim_max ( ) ,
124- } ;
125- if let Some ( v) = trimmed {
126- v
127- } else {
128- continue ;
129- }
130- } else {
131- continue ;
132- }
133- }
134- }
97+ let Some ( raw_min) = mins. index ( 0 ) . map ( |v| v. to_owned ( ) ) else {
98+ continue ;
99+ } ;
100+ let Some ( raw_max) = maxs. index ( 0 ) . map ( |v| v. to_owned ( ) ) else {
101+ continue ;
102+ } ;
103+ let Some ( min_max) = trim_column_min_max (
104+ raw_min,
105+ raw_max,
106+ col_stats_truncate_lens. get ( & column_id) . copied ( ) ,
107+ ) else {
108+ continue ;
109+ } ;
110+ min_max
111+ } else {
112+ ( Scalar :: Null , Scalar :: Null )
113+ } ;
135114
136115 let ( is_all_null, bitmap) = col. validity ( ) ;
137116 let unset_bits = match ( is_all_null, bitmap) {
@@ -190,6 +169,43 @@ pub trait Trim: Sized {
190169
191170pub const END_OF_UNICODE_RANGE : char = '\u{10FFFF}' ;
192171pub const STATS_STRING_PREFIX_LEN : usize = 16 ;
172+ const MAX_AUTO_STATS_STRING_PREFIX_LEN : usize = 32 ;
173+ const MAX_AUTO_STATS_STRING_COMMON_PREFIX_LEN : usize = MAX_AUTO_STATS_STRING_PREFIX_LEN - 1 ;
174+
175+ fn auto_stats_string_prefix_len ( min : & str , max : & str ) -> usize {
176+ let common_prefix_len = min
177+ . chars ( )
178+ . zip ( max. chars ( ) )
179+ . take ( MAX_AUTO_STATS_STRING_COMMON_PREFIX_LEN )
180+ . take_while ( |( min_char, max_char) | min_char == max_char)
181+ . count ( ) ;
182+
183+ if common_prefix_len < STATS_STRING_PREFIX_LEN {
184+ STATS_STRING_PREFIX_LEN
185+ } else {
186+ common_prefix_len
187+ . saturating_add ( 1 )
188+ . min ( MAX_AUTO_STATS_STRING_PREFIX_LEN )
189+ }
190+ }
191+
192+ pub ( crate ) fn trim_column_min_max (
193+ min : Scalar ,
194+ max : Scalar ,
195+ string_truncate_len : Option < usize > ,
196+ ) -> Option < ( Scalar , Scalar ) > {
197+ match ( min, max) {
198+ ( Scalar :: String ( min) , Scalar :: String ( max) ) => {
199+ let truncate_len =
200+ string_truncate_len. unwrap_or_else ( || auto_stats_string_prefix_len ( & min, & max) ) ;
201+ Some ( (
202+ Scalar :: String ( trim_string_min_with_len ( min, truncate_len) ?) ,
203+ Scalar :: String ( trim_string_max_with_len ( max, truncate_len) ?) ,
204+ ) )
205+ }
206+ ( min, max) => Some ( ( min. trim_min ( ) ?, max. trim_max ( ) ?) ) ,
207+ }
208+ }
193209
194210impl Trim for Scalar {
195211 fn trim_min ( self ) -> Option < Self > {
@@ -317,10 +333,48 @@ pub fn trim_string_max_with_len(s: String, len: usize) -> Option<String> {
317333mod tests {
318334 use databend_common_expression:: Scalar ;
319335
336+ use super :: MAX_AUTO_STATS_STRING_COMMON_PREFIX_LEN ;
337+ use super :: MAX_AUTO_STATS_STRING_PREFIX_LEN ;
338+ use super :: auto_stats_string_prefix_len;
320339 use crate :: statistics:: END_OF_UNICODE_RANGE ;
321340 use crate :: statistics:: STATS_STRING_PREFIX_LEN ;
322341 use crate :: statistics:: Trim ;
323342
343+ #[ test]
344+ fn test_auto_stats_string_prefix_len ( ) {
345+ assert_eq ! (
346+ auto_stats_string_prefix_len( "abcdefghijklmnoa" , "abcdefghijklmnoz" ) ,
347+ STATS_STRING_PREFIX_LEN
348+ ) ;
349+
350+ let prefix = "abcdefghijklmnop" ;
351+ assert_eq ! (
352+ auto_stats_string_prefix_len(
353+ & format!( "{prefix}a-min-suffix" ) ,
354+ & format!( "{prefix}z-max-suffix" ) ,
355+ ) ,
356+ STATS_STRING_PREFIX_LEN + 1
357+ ) ;
358+
359+ let long_prefix = "a" . repeat ( MAX_AUTO_STATS_STRING_COMMON_PREFIX_LEN ) ;
360+ assert_eq ! (
361+ auto_stats_string_prefix_len(
362+ & format!( "{long_prefix}x-min" ) ,
363+ & format!( "{long_prefix}y-max" ) ,
364+ ) ,
365+ MAX_AUTO_STATS_STRING_PREFIX_LEN
366+ ) ;
367+
368+ let capped_prefix = "好" . repeat ( MAX_AUTO_STATS_STRING_PREFIX_LEN ) ;
369+ assert_eq ! (
370+ auto_stats_string_prefix_len(
371+ & format!( "{capped_prefix}甲" ) ,
372+ & format!( "{capped_prefix}乙" ) ,
373+ ) ,
374+ MAX_AUTO_STATS_STRING_PREFIX_LEN
375+ ) ;
376+ }
377+
324378 #[ test]
325379 fn test_trim_max ( ) {
326380 {
0 commit comments