• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 17068274397

19 Aug 2025 11:32AM UTC coverage: 87.857% (-0.03%) from 87.883%
17068274397

push

github

web-flow
Add Display impl for FieldNames (#4282)

Yet another step in my journey with this type

Signed-off-by: Adam Gutglick <adam@spiraldb.com>

11 of 11 new or added lines in 1 file covered. (100.0%)

12 existing lines in 2 files now uncovered.

56588 of 64409 relevant lines covered (87.86%)

625623.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/vortex-array/src/stats/array.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
//! Stats as they are stored on arrays.
5

6
use std::sync::Arc;
7

8
use parking_lot::RwLock;
9
use vortex_error::{VortexError, VortexResult, vortex_panic};
10
use vortex_scalar::{Scalar, ScalarValue};
11

12
use super::{Precision, Stat, StatsProvider, StatsSet, StatsSetIntoIter, TypedStatsSetRef};
13
use crate::Array;
14
use crate::compute::{
15
    MinMaxResult, is_constant, is_sorted, is_strict_sorted, min_max, nan_count, sum,
16
};
17

18
/// A shared [`StatsSet`] stored in an array. Can be shared by copies of the array and can also be mutated in place.
19
// TODO(adamg): This is a very bad name.
20
#[derive(Clone, Default, Debug)]
21
pub struct ArrayStats {
22
    inner: Arc<RwLock<StatsSet>>,
23
}
24

25
/// Reference to an array's [`StatsSet`]. Can be used to get and mutate the underlying stats.
26
///
27
/// Constructed by calling [`ArrayStats::to_ref`].
28
pub struct StatsSetRef<'a> {
29
    // We need to reference back to the array
30
    dyn_array_ref: &'a dyn Array,
31
    array_stats: &'a ArrayStats,
32
}
33

34
impl ArrayStats {
35
    pub fn to_ref<'a>(&'a self, array: &'a dyn Array) -> StatsSetRef<'a> {
19,541,129✔
36
        StatsSetRef {
19,541,129✔
37
            dyn_array_ref: array,
19,541,129✔
38
            array_stats: self,
19,541,129✔
39
        }
19,541,129✔
40
    }
19,541,129✔
41

42
    pub fn set(&self, stat: Stat, value: Precision<ScalarValue>) {
655,474✔
43
        self.inner.write().set(stat, value);
655,474✔
44
    }
655,474✔
45

46
    pub fn clear(&self, stat: Stat) {
×
47
        self.inner.write().clear(stat);
×
48
    }
×
49

50
    pub fn retain(&self, stats: &[Stat]) {
×
51
        self.inner.write().retain_only(stats);
×
52
    }
×
53
}
54

55
impl From<StatsSet> for ArrayStats {
56
    fn from(value: StatsSet) -> Self {
×
57
        Self {
×
58
            inner: Arc::new(RwLock::new(value)),
×
59
        }
×
60
    }
×
61
}
62

63
impl From<ArrayStats> for StatsSet {
64
    fn from(value: ArrayStats) -> Self {
×
65
        value.inner.read().clone()
×
66
    }
×
67
}
68

69
impl StatsSetRef<'_> {
70
    pub fn set_iter(&self, iter: StatsSetIntoIter) {
39✔
71
        let mut guard = self.array_stats.inner.write();
39✔
72
        for (stat, value) in iter {
312✔
73
            guard.set(stat, value);
273✔
74
        }
273✔
75
    }
39✔
76

77
    pub fn inherit_from(&self, stats: StatsSetRef<'_>) {
4,724,910✔
78
        // Only inherit if the underlying stats are different
79
        if !Arc::ptr_eq(&self.array_stats.inner, &stats.array_stats.inner) {
4,724,910✔
80
            stats.with_iter(|iter| self.inherit(iter));
1,243,184✔
81
        }
3,481,726✔
82
    }
4,724,910✔
83

84
    pub fn inherit<'a>(&self, iter: impl Iterator<Item = &'a (Stat, Precision<ScalarValue>)>) {
3,829,408✔
85
        let mut guard = self.array_stats.inner.write();
3,829,408✔
86
        for (stat, value) in iter {
3,901,702✔
87
            if !value.is_exact() {
72,294✔
UNCOV
88
                if !guard.get(*stat).is_some_and(|v| v.is_exact()) {
×
UNCOV
89
                    guard.set(*stat, value.clone());
×
UNCOV
90
                }
×
91
            } else {
72,294✔
92
                guard.set(*stat, value.clone());
72,294✔
93
            }
72,294✔
94
        }
95
    }
3,829,408✔
96

97
    pub fn with_typed_stats_set<U, F: FnOnce(TypedStatsSetRef) -> U>(&self, apply: F) -> U {
31,172✔
98
        apply(
31,172✔
99
            self.array_stats
31,172✔
100
                .inner
31,172✔
101
                .read()
31,172✔
102
                .as_typed_ref(self.dyn_array_ref.dtype()),
31,172✔
103
        )
31,172✔
104
    }
31,172✔
105

106
    pub fn to_owned(&self) -> StatsSet {
237,018✔
107
        self.array_stats.inner.read().clone()
237,018✔
108
    }
237,018✔
109

110
    pub fn with_iter<
3,829,408✔
111
        F: for<'a> FnOnce(&mut dyn Iterator<Item = &'a (Stat, Precision<ScalarValue>)>) -> R,
3,829,408✔
112
        R,
3,829,408✔
113
    >(
3,829,408✔
114
        &self,
3,829,408✔
115
        f: F,
3,829,408✔
116
    ) -> R {
3,829,408✔
117
        let lock = self.array_stats.inner.read();
3,829,408✔
118
        f(&mut lock.iter())
3,829,408✔
119
    }
3,829,408✔
120

121
    pub fn compute_stat(&self, stat: Stat) -> VortexResult<Option<Scalar>> {
331,151✔
122
        // If it's already computed and exact, we can return it.
123
        if let Some(Precision::Exact(s)) = self.get(stat) {
331,151✔
124
            return Ok(Some(s));
202,703✔
125
        }
128,448✔
126

127
        Ok(match stat {
128,448✔
128
            Stat::Min => min_max(self.dyn_array_ref)?.map(|MinMaxResult { min, max: _ }| min),
56,680✔
129
            Stat::Max => min_max(self.dyn_array_ref)?.map(|MinMaxResult { min: _, max }| max),
8,146✔
130
            Stat::Sum => {
131
                Stat::Sum
15,094✔
132
                    .dtype(self.dyn_array_ref.dtype())
15,094✔
133
                    .is_some()
15,094✔
134
                    .then(|| {
15,094✔
135
                        // Sum is supported for this dtype.
136
                        sum(self.dyn_array_ref)
6,972✔
137
                    })
6,972✔
138
                    .transpose()?
15,094✔
139
            }
140
            Stat::NullCount => Some(self.dyn_array_ref.invalid_count()?.into()),
5,813✔
141
            Stat::IsConstant => {
142
                if self.dyn_array_ref.is_empty() {
7,164✔
143
                    None
39✔
144
                } else {
145
                    is_constant(self.dyn_array_ref)?.map(|v| v.into())
7,125✔
146
                }
147
            }
148
            Stat::IsSorted => Some(is_sorted(self.dyn_array_ref)?.into()),
6,778✔
149
            Stat::IsStrictSorted => Some(is_strict_sorted(self.dyn_array_ref)?.into()),
7,899✔
150
            Stat::UncompressedSizeInBytes => {
151
                let nbytes = self.dyn_array_ref.to_canonical()?.as_ref().nbytes();
6,778✔
152
                self.set(stat, Precision::exact(nbytes));
6,778✔
153
                Some(nbytes.into())
6,778✔
154
            }
155
            Stat::NaNCount => {
156
                Stat::NaNCount
14,096✔
157
                    .dtype(self.dyn_array_ref.dtype())
14,096✔
158
                    .is_some()
14,096✔
159
                    .then(|| {
14,096✔
160
                        // NaNCount is supported for this dtype.
161
                        nan_count(self.dyn_array_ref)
47✔
162
                    })
47✔
163
                    .transpose()?
14,096✔
164
                    .map(|s| s.into())
14,096✔
165
            }
166
        })
167
    }
331,151✔
168

169
    pub fn compute_all(&self, stats: &[Stat]) -> VortexResult<StatsSet> {
14,136✔
170
        let mut stats_set = StatsSet::default();
14,136✔
171
        for &stat in stats {
111,914✔
172
            if let Some(s) = self.compute_stat(stat)? {
97,778✔
173
                stats_set.set(stat, Precision::exact(s.into_value()))
69,422✔
174
            }
28,356✔
175
        }
176
        Ok(stats_set)
14,136✔
177
    }
14,136✔
178
}
179

180
impl StatsSetRef<'_> {
181
    pub fn compute_as<U: for<'a> TryFrom<&'a Scalar, Error = VortexError>>(
136,422✔
182
        &self,
136,422✔
183
        stat: Stat,
136,422✔
184
    ) -> Option<U> {
136,422✔
185
        self.compute_stat(stat)
136,422✔
186
            .inspect_err(|e| log::warn!("Failed to compute stat {stat}: {e}"))
136,422✔
187
            .ok()
136,422✔
188
            .flatten()
136,422✔
189
            .map(|s| U::try_from(&s))
136,422✔
190
            .transpose()
136,422✔
191
            .unwrap_or_else(|err| {
136,422✔
192
                vortex_panic!(
×
UNCOV
193
                    err,
×
UNCOV
194
                    "Failed to compute stat {} as {}",
×
195
                    stat,
UNCOV
196
                    std::any::type_name::<U>()
×
197
                )
198
            })
199
    }
136,422✔
200

201
    pub fn set(&self, stat: Stat, value: Precision<ScalarValue>) {
655,474✔
202
        self.array_stats.set(stat, value);
655,474✔
203
    }
655,474✔
204

205
    pub fn clear(&self, stat: Stat) {
×
206
        self.array_stats.clear(stat);
×
207
    }
×
208

209
    pub fn retain(&self, stats: &[Stat]) {
×
UNCOV
210
        self.array_stats.retain(stats);
×
UNCOV
211
    }
×
212

213
    pub fn compute_min<U: for<'a> TryFrom<&'a Scalar, Error = VortexError>>(&self) -> Option<U> {
23,777✔
214
        self.compute_as(Stat::Min)
23,777✔
215
    }
23,777✔
216

217
    pub fn compute_max<U: for<'a> TryFrom<&'a Scalar, Error = VortexError>>(&self) -> Option<U> {
18✔
218
        self.compute_as(Stat::Max)
18✔
219
    }
18✔
220

UNCOV
221
    pub fn compute_is_sorted(&self) -> Option<bool> {
×
222
        self.compute_as(Stat::IsSorted)
×
223
    }
×
224

225
    pub fn compute_is_strict_sorted(&self) -> Option<bool> {
17,145✔
226
        self.compute_as(Stat::IsStrictSorted)
17,145✔
227
    }
17,145✔
228

229
    pub fn compute_is_constant(&self) -> Option<bool> {
464✔
230
        self.compute_as(Stat::IsConstant)
464✔
231
    }
464✔
232

233
    pub fn compute_null_count(&self) -> Option<usize> {
11,946✔
234
        self.compute_as(Stat::NullCount)
11,946✔
235
    }
11,946✔
236

UNCOV
237
    pub fn compute_uncompressed_size_in_bytes(&self) -> Option<usize> {
×
238
        self.compute_as(Stat::UncompressedSizeInBytes)
×
239
    }
×
240
}
241

242
impl StatsProvider for StatsSetRef<'_> {
243
    fn get(&self, stat: Stat) -> Option<Precision<Scalar>> {
2,371,143✔
244
        self.array_stats
2,371,143✔
245
            .inner
2,371,143✔
246
            .read()
2,371,143✔
247
            .as_typed_ref(self.dyn_array_ref.dtype())
2,371,143✔
248
            .get(stat)
2,371,143✔
249
    }
2,371,143✔
250

251
    fn len(&self) -> usize {
×
252
        self.array_stats.inner.read().len()
×
UNCOV
253
    }
×
254
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc