/src/duckdb/src/storage/table/table_statistics.cpp
Line | Count | Source |
1 | | #include "duckdb/storage/table/table_statistics.hpp" |
2 | | |
3 | | #include "duckdb/common/serializer/deserializer.hpp" |
4 | | #include "duckdb/common/serializer/serializer.hpp" |
5 | | #include "duckdb/execution/reservoir_sample.hpp" |
6 | | #include "duckdb/storage/table/persistent_table_data.hpp" |
7 | | #include "duckdb/parser/column_list.hpp" |
8 | | |
9 | | namespace duckdb { |
10 | | |
11 | 0 | void TableStatistics::Initialize(const vector<LogicalType> &types, PersistentTableData &data) { |
12 | 0 | D_ASSERT(Empty()); |
13 | 0 | D_ASSERT(!table_sample); |
14 | |
|
15 | 0 | stats_lock = make_shared_ptr<mutex>(); |
16 | 0 | column_stats = std::move(data.table_stats.column_stats); |
17 | 0 | if (data.table_stats.table_sample) { |
18 | 0 | table_sample = std::move(data.table_stats.table_sample); |
19 | 0 | } else { |
20 | 0 | table_sample = make_uniq<ReservoirSample>(static_cast<idx_t>(FIXED_SAMPLE_SIZE)); |
21 | 0 | } |
22 | 0 | if (column_stats.size() != types.size()) { // LCOV_EXCL_START |
23 | 0 | throw IOException("Table statistics column count is not aligned with table column count. Corrupt file?"); |
24 | 0 | } // LCOV_EXCL_STOP |
25 | 0 | } |
26 | | |
27 | 0 | void TableStatistics::InitializeEmpty(const TableStatistics &other) { |
28 | 0 | D_ASSERT(Empty()); |
29 | 0 | D_ASSERT(!table_sample); |
30 | |
|
31 | 0 | stats_lock = make_shared_ptr<mutex>(); |
32 | 0 | if (other.table_sample) { |
33 | 0 | D_ASSERT(other.table_sample->type == SampleType::RESERVOIR_SAMPLE); |
34 | 0 | auto &res = other.table_sample->Cast<ReservoirSample>(); |
35 | 0 | table_sample = res.Copy(); |
36 | 0 | } else { |
37 | 0 | table_sample = make_uniq<ReservoirSample>(static_cast<idx_t>(FIXED_SAMPLE_SIZE)); |
38 | 0 | } |
39 | |
|
40 | 0 | for (auto &stats : other.column_stats) { |
41 | 0 | auto new_column_stats = ColumnStatistics::CreateEmptyStats(stats->Statistics().GetType()); |
42 | 0 | if (stats->HasDistinctStats()) { |
43 | 0 | new_column_stats->SetDistinct(stats->DistinctStats().Copy()); |
44 | 0 | } |
45 | |
|
46 | 0 | auto &base_stats = new_column_stats->Statistics(); |
47 | 0 | if (new_column_stats->HasDistinctStats()) { |
48 | 0 | base_stats.SetDistinctCount(new_column_stats->DistinctStats().GetCount()); |
49 | 0 | } |
50 | 0 | column_stats.push_back(new_column_stats); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | 0 | void TableStatistics::InitializeEmpty(const vector<LogicalType> &types) { |
55 | 0 | D_ASSERT(Empty()); |
56 | 0 | D_ASSERT(!table_sample); |
57 | |
|
58 | 0 | stats_lock = make_shared_ptr<mutex>(); |
59 | 0 | table_sample = make_uniq<ReservoirSample>(static_cast<idx_t>(FIXED_SAMPLE_SIZE)); |
60 | 0 | for (auto &type : types) { |
61 | 0 | column_stats.push_back(ColumnStatistics::CreateEmptyStats(type)); |
62 | 0 | } |
63 | 0 | } |
64 | | |
65 | 0 | void TableStatistics::InitializeAddColumn(TableStatistics &parent, const LogicalType &new_column_type) { |
66 | 0 | D_ASSERT(Empty()); |
67 | 0 | D_ASSERT(parent.stats_lock); |
68 | |
|
69 | 0 | stats_lock = parent.stats_lock; |
70 | 0 | lock_guard<mutex> lock(*stats_lock); |
71 | 0 | for (idx_t i = 0; i < parent.column_stats.size(); i++) { |
72 | 0 | column_stats.push_back(parent.column_stats[i]); |
73 | 0 | } |
74 | 0 | column_stats.push_back(ColumnStatistics::CreateEmptyStats(new_column_type)); |
75 | 0 | if (parent.table_sample) { |
76 | 0 | table_sample = std::move(parent.table_sample); |
77 | 0 | } |
78 | 0 | if (table_sample) { |
79 | 0 | table_sample->Destroy(); |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | 0 | void TableStatistics::InitializeRemoveColumn(TableStatistics &parent, idx_t removed_column) { |
84 | 0 | D_ASSERT(Empty()); |
85 | 0 | D_ASSERT(parent.stats_lock); |
86 | |
|
87 | 0 | stats_lock = parent.stats_lock; |
88 | 0 | lock_guard<mutex> lock(*stats_lock); |
89 | 0 | for (idx_t i = 0; i < parent.column_stats.size(); i++) { |
90 | 0 | if (i != removed_column) { |
91 | 0 | column_stats.push_back(parent.column_stats[i]); |
92 | 0 | } |
93 | 0 | } |
94 | 0 | if (parent.table_sample) { |
95 | 0 | table_sample = std::move(parent.table_sample); |
96 | 0 | } |
97 | 0 | if (table_sample) { |
98 | 0 | table_sample->Destroy(); |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | 0 | void TableStatistics::InitializeAlterType(TableStatistics &parent, idx_t changed_idx, const LogicalType &new_type) { |
103 | 0 | D_ASSERT(Empty()); |
104 | 0 | D_ASSERT(parent.stats_lock); |
105 | |
|
106 | 0 | stats_lock = parent.stats_lock; |
107 | 0 | lock_guard<mutex> lock(*stats_lock); |
108 | 0 | for (idx_t i = 0; i < parent.column_stats.size(); i++) { |
109 | 0 | if (i == changed_idx) { |
110 | 0 | column_stats.push_back(ColumnStatistics::CreateEmptyStats(new_type)); |
111 | 0 | } else { |
112 | 0 | column_stats.push_back(parent.column_stats[i]); |
113 | 0 | } |
114 | 0 | } |
115 | 0 | if (parent.table_sample) { |
116 | 0 | table_sample = std::move(parent.table_sample); |
117 | 0 | } |
118 | 0 | if (table_sample) { |
119 | 0 | table_sample->Destroy(); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | 0 | void TableStatistics::InitializeAddConstraint(TableStatistics &parent) { |
124 | 0 | D_ASSERT(Empty()); |
125 | 0 | D_ASSERT(parent.stats_lock); |
126 | |
|
127 | 0 | stats_lock = parent.stats_lock; |
128 | 0 | lock_guard<mutex> lock(*stats_lock); |
129 | 0 | for (idx_t i = 0; i < parent.column_stats.size(); i++) { |
130 | 0 | column_stats.push_back(parent.column_stats[i]); |
131 | 0 | } |
132 | 0 | if (parent.table_sample) { |
133 | 0 | table_sample = std::move(parent.table_sample); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | 0 | void TableStatistics::MergeStats(TableStatistics &other) { |
138 | 0 | auto l = GetLock(); |
139 | 0 | D_ASSERT(column_stats.size() == other.column_stats.size()); |
140 | 0 | if (table_sample) { |
141 | 0 | if (other.table_sample) { |
142 | 0 | D_ASSERT(table_sample->type == SampleType::RESERVOIR_SAMPLE); |
143 | 0 | auto &this_reservoir = table_sample->Cast<ReservoirSample>(); |
144 | 0 | D_ASSERT(other.table_sample->type == SampleType::RESERVOIR_SAMPLE); |
145 | 0 | this_reservoir.Merge(std::move(other.table_sample)); |
146 | 0 | } |
147 | | // if no other.table sample, do nothing |
148 | 0 | } else { |
149 | 0 | if (other.table_sample) { |
150 | 0 | auto &other_reservoir = other.table_sample->Cast<ReservoirSample>(); |
151 | 0 | auto other_table_sample_copy = other_reservoir.Copy(); |
152 | 0 | table_sample = std::move(other_table_sample_copy); |
153 | 0 | } |
154 | 0 | } |
155 | 0 | for (idx_t i = 0; i < column_stats.size(); i++) { |
156 | 0 | if (column_stats[i]) { |
157 | 0 | D_ASSERT(other.column_stats[i]); |
158 | 0 | column_stats[i]->Merge(*other.column_stats[i]); |
159 | 0 | } |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | 0 | void TableStatistics::MergeStats(idx_t i, BaseStatistics &stats) { |
164 | 0 | auto l = GetLock(); |
165 | 0 | MergeStats(*l, i, stats); |
166 | 0 | } |
167 | | |
168 | 0 | void TableStatistics::MergeStats(TableStatisticsLock &lock, idx_t i, BaseStatistics &stats, StatsMergeType merge_type) { |
169 | 0 | column_stats[i]->Statistics().Merge(stats, merge_type); |
170 | 0 | } |
171 | | |
172 | 0 | ColumnStatistics &TableStatistics::GetStats(TableStatisticsLock &lock, idx_t i) { |
173 | 0 | return *column_stats[i]; |
174 | 0 | } |
175 | | |
176 | | // BlockingSample &TableStatistics::GetTableSampleRef(TableStatisticsLock &lock) { |
177 | | // D_ASSERT(table_sample); |
178 | | // return *table_sample; |
179 | | //} |
180 | | |
181 | 0 | unique_ptr<BlockingSample> TableStatistics::GetTableSample(TableStatisticsLock &lock) { |
182 | 0 | return std::move(table_sample); |
183 | 0 | } |
184 | | |
185 | 0 | void TableStatistics::SetTableSample(TableStatisticsLock &lock, unique_ptr<BlockingSample> sample) { |
186 | 0 | table_sample = std::move(sample); |
187 | 0 | } |
188 | | |
189 | 0 | void TableStatistics::DestroyTableSample(TableStatisticsLock &lock) const { |
190 | 0 | if (table_sample) { |
191 | 0 | table_sample->Destroy(); |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | 0 | void TableStatistics::SetStats(TableStatistics &other) { |
196 | 0 | TableStatisticsLock lock(*stats_lock); |
197 | 0 | column_stats = std::move(other.column_stats); |
198 | 0 | table_sample = std::move(other.table_sample); |
199 | 0 | } |
200 | | |
201 | 0 | unique_ptr<BaseStatistics> TableStatistics::CopyStats(const StorageIndex &index) { |
202 | 0 | lock_guard<mutex> l(*stats_lock); |
203 | |
|
204 | 0 | auto column_index = index.GetPrimaryIndex(); |
205 | 0 | auto &stats = *column_stats[column_index]; |
206 | 0 | auto result = stats.Statistics().Copy(); |
207 | 0 | if (stats.HasDistinctStats()) { |
208 | 0 | result.SetDistinctCount(stats.DistinctStats().GetCount()); |
209 | 0 | } |
210 | 0 | if (index.IsPushdownExtract()) { |
211 | 0 | return result.PushdownExtract(index.GetChildIndexes()[0]); |
212 | 0 | } |
213 | 0 | return result.ToUnique(); |
214 | 0 | } |
215 | | |
216 | 0 | void TableStatistics::CopyStats(TableStatistics &other) { |
217 | 0 | TableStatisticsLock lock(*stats_lock); |
218 | 0 | CopyStats(lock, other); |
219 | 0 | } |
220 | | |
221 | 0 | void TableStatistics::CopyStats(TableStatisticsLock &lock, TableStatistics &other) { |
222 | 0 | D_ASSERT(other.Empty()); |
223 | 0 | other.stats_lock = make_shared_ptr<mutex>(); |
224 | 0 | for (auto &stats : column_stats) { |
225 | 0 | other.column_stats.push_back(stats->Copy()); |
226 | 0 | } |
227 | |
|
228 | 0 | if (table_sample) { |
229 | 0 | D_ASSERT(table_sample->type == SampleType::RESERVOIR_SAMPLE); |
230 | 0 | auto &res = table_sample->Cast<ReservoirSample>(); |
231 | 0 | other.table_sample = res.Copy(); |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | 0 | void TableStatistics::Serialize(Serializer &serializer) const { |
236 | 0 | serializer.WriteProperty(100, "column_stats", column_stats); |
237 | 0 | unique_ptr<BlockingSample> to_serialize = nullptr; |
238 | 0 | if (table_sample) { |
239 | 0 | D_ASSERT(table_sample->type == SampleType::RESERVOIR_SAMPLE); |
240 | 0 | auto &reservoir_sample = table_sample->Cast<ReservoirSample>(); |
241 | 0 | to_serialize = unique_ptr_cast<BlockingSample, ReservoirSample>(reservoir_sample.Copy()); |
242 | 0 | auto &res_serialize = to_serialize->Cast<ReservoirSample>(); |
243 | 0 | res_serialize.EvictOverBudgetSamples(); |
244 | 0 | } |
245 | 0 | serializer.WritePropertyWithDefault<unique_ptr<BlockingSample>>(101, "table_sample", to_serialize, nullptr); |
246 | 0 | } |
247 | | |
248 | 0 | void TableStatistics::Deserialize(Deserializer &deserializer, ColumnList &columns) { |
249 | 0 | auto physical_columns = columns.Physical(); |
250 | |
|
251 | 0 | auto iter = physical_columns.begin(); |
252 | 0 | deserializer.ReadList(100, "column_stats", [&](Deserializer::List &list, idx_t i) { |
253 | 0 | auto &col = *iter; |
254 | 0 | iter.operator++(); |
255 | |
|
256 | 0 | auto type = col.GetType(); |
257 | 0 | deserializer.Set<LogicalType &>(type); |
258 | |
|
259 | 0 | column_stats.push_back(list.ReadElement<shared_ptr<ColumnStatistics>>()); |
260 | |
|
261 | 0 | deserializer.Unset<LogicalType>(); |
262 | 0 | }); |
263 | 0 | table_sample = deserializer.ReadPropertyWithDefault<unique_ptr<BlockingSample>>(101, "table_sample"); |
264 | 0 | if (table_sample) { |
265 | 0 | D_ASSERT(table_sample->type == SampleType::RESERVOIR_SAMPLE); |
266 | | #ifdef DEBUG |
267 | | if (table_sample) { |
268 | | auto &reservoir_sample = table_sample->Cast<ReservoirSample>(); |
269 | | reservoir_sample.Verify(); |
270 | | } |
271 | | #endif |
272 | 0 | } else { |
273 | 0 | table_sample = make_uniq<ReservoirSample>(static_cast<idx_t>(FIXED_SAMPLE_SIZE)); |
274 | 0 | table_sample->Destroy(); |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | 0 | unique_ptr<TableStatisticsLock> TableStatistics::GetLock() { |
279 | 0 | D_ASSERT(stats_lock); |
280 | 0 | return make_uniq<TableStatisticsLock>(*stats_lock); |
281 | 0 | } |
282 | | |
283 | 0 | bool TableStatistics::Empty() const { |
284 | 0 | D_ASSERT(column_stats.empty() == (stats_lock.get() == nullptr)); |
285 | 0 | return column_stats.empty(); |
286 | 0 | } |
287 | | |
288 | | } // namespace duckdb |