be/src/storage/segment/column_writer.h
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #pragma once |
19 | | |
20 | | #include <gen_cpp/AgentService_types.h> |
21 | | #include <gen_cpp/olap_file.pb.h> |
22 | | #include <gen_cpp/segment_v2.pb.h> |
23 | | #include <stddef.h> |
24 | | #include <stdint.h> |
25 | | |
26 | | #include <algorithm> |
27 | | #include <functional> |
28 | | #include <memory> // for unique_ptr |
29 | | #include <ostream> |
30 | | #include <span> |
31 | | #include <string> |
32 | | #include <unordered_map> |
33 | | #include <utility> |
34 | | #include <vector> |
35 | | |
36 | | #include "common/status.h" // for Status |
37 | | #include "core/column/column.h" |
38 | | #include "core/column/column_vector.h" |
39 | | #include "storage/index/ann/ann_index_writer.h" |
40 | | #include "storage/index/bloom_filter/bloom_filter.h" |
41 | | #include "storage/index/inverted/clucene/inverted_index_writer.h" |
42 | | #include "storage/segment/common.h" |
43 | | #include "storage/segment/options.h" |
44 | | #include "storage/segment/variant/nested_group_provider.h" |
45 | | #include "storage/segment/variant/variant_statistics.h" |
46 | | #include "storage/storage_layout.h" // for StorageBuffers |
47 | | #include "storage/tablet/tablet_schema.h" // for TabletColumnPtr |
48 | | #include "util/slice.h" // for OwnedSlice |
49 | | |
50 | | namespace doris { |
51 | | |
52 | | class BlockCompressionCodec; |
53 | | class TabletColumn; |
54 | | class TabletIndex; |
55 | | struct RowsetWriterContext; |
56 | | |
57 | | namespace io { |
58 | | class FileWriter; |
59 | | } |
60 | | |
61 | | namespace segment_v2 { |
62 | | |
63 | | enum class VariantWriterInputFormat : uint8_t { |
64 | | UNSET, |
65 | | V2, |
66 | | }; |
67 | | |
68 | | struct ColumnWriterOptions { |
69 | | // input and output parameter: |
70 | | // - input: column_id/unique_id/type/length/encoding/compression/is_nullable members |
71 | | // - output: encoding/indexes/dict_page members |
72 | | ColumnMetaPB* meta = nullptr; |
73 | | size_t data_page_size = STORAGE_PAGE_SIZE_DEFAULT_VALUE; |
74 | | size_t dict_page_size = STORAGE_DICT_PAGE_SIZE_DEFAULT_VALUE; |
75 | | // store compressed page only when space saving is above the threshold. |
76 | | // space saving = 1 - compressed_size / uncompressed_size |
77 | | double compression_min_space_saving = 0.1; |
78 | | bool need_zone_map = false; |
79 | | bool need_bloom_filter = false; |
80 | | bool is_ngram_bf_index = false; |
81 | | bool need_inverted_index = false; |
82 | | bool need_ann_index = false; |
83 | | uint8_t gram_size; |
84 | | uint16_t gram_bf_size; |
85 | | BloomFilterOptions bf_options; |
86 | | std::vector<const TabletIndex*> inverted_indexes; |
87 | | IndexFileWriter* index_file_writer = nullptr; |
88 | | // The owning segment serves a direct load (stream/broker load, |
89 | | // DataWriteType::TYPE_DIRECT) rather than compaction / schema change. Set |
90 | | // once by the segment writer and propagated to variant subcolumn writers; |
91 | | // forwarded to every created IndexColumnWriter via set_direct_load() so |
92 | | // SNII can select its direct-load PRX zstd level without plumbing |
93 | | // DataWriteType itself down here. |
94 | | bool is_direct_load = false; |
95 | | |
96 | | SegmentFooterPB* footer = nullptr; |
97 | | io::FileWriter* file_writer = nullptr; |
98 | | CompressionTypePB compression_type = UNKNOWN_COMPRESSION; |
99 | | RowsetWriterContext* rowset_ctx = nullptr; |
100 | | // For collect segment statistics for compaction |
101 | | std::vector<RowsetReaderSharedPtr> input_rs_readers; |
102 | | const TabletIndex* ann_index = nullptr; |
103 | | |
104 | | // Storage format of the owning tablet (V2 or V3). Set once by the segment writer |
105 | | // (from TabletMeta::storage_format()) and propagated down to aux child writers |
106 | | // (null / array-length / map-length), struct subcolumn writers and variant subcolumn |
107 | | // writers. All encoding-default decisions consult this via resolve_default_encoding(). |
108 | | // Also forwarded to BinaryDictPageBuilder via PageBuilderOptions::binary_plain_encoding. |
109 | | TabletStorageFormatPB storage_format = TabletStorageFormatPB::TABLET_STORAGE_FORMAT_V2; |
110 | | |
111 | 0 | std::string to_string() const { |
112 | 0 | std::stringstream ss; |
113 | 0 | ss << std::boolalpha << "meta=" << meta->DebugString() |
114 | 0 | << ", data_page_size=" << data_page_size << ", dict_page_size=" << dict_page_size |
115 | 0 | << ", compression_min_space_saving = " << compression_min_space_saving |
116 | 0 | << ", need_zone_map=" << need_zone_map << ", need_bloom_filter" << need_bloom_filter; |
117 | 0 | return ss.str(); |
118 | 0 | } |
119 | | }; |
120 | | |
121 | | class EncodingInfo; |
122 | | class NullBitmapBuilder; |
123 | | class OrdinalIndexWriter; |
124 | | class PageBuilder; |
125 | | class BloomFilterIndexWriter; |
126 | | class ZoneMapIndexWriter; |
127 | | class VariantColumnWriterImpl; |
128 | | class VariantShredder; |
129 | | class VariantPathBuilder; |
130 | | class ColumnWriter; |
131 | | |
132 | | // Base interface for scalar, ARRAY, MAP, STRUCT, AGG_STATE and VARIANT columns. |
133 | | class ColumnWriter { |
134 | | public: |
135 | | static Status create(const ColumnWriterOptions& opts, const TabletColumn* column, |
136 | | io::FileWriter* file_writer, std::unique_ptr<ColumnWriter>* writer); |
137 | | static Status create_struct_writer(const ColumnWriterOptions& opts, const TabletColumn* column, |
138 | | io::FileWriter* file_writer, |
139 | | std::unique_ptr<ColumnWriter>* writer); |
140 | | static Status create_array_writer(const ColumnWriterOptions& opts, const TabletColumn* column, |
141 | | io::FileWriter* file_writer, |
142 | | std::unique_ptr<ColumnWriter>* writer); |
143 | | static Status create_map_writer(const ColumnWriterOptions& opts, const TabletColumn* column, |
144 | | io::FileWriter* file_writer, |
145 | | std::unique_ptr<ColumnWriter>* writer); |
146 | | |
147 | | static Status create_variant_writer(const ColumnWriterOptions& opts, const TabletColumn* column, |
148 | | io::FileWriter* file_writer, |
149 | | std::unique_ptr<ColumnWriter>* writer); |
150 | | |
151 | | static Status create_agg_state_writer(const ColumnWriterOptions& opts, |
152 | | const TabletColumn* column, io::FileWriter* file_writer, |
153 | | std::unique_ptr<ColumnWriter>* writer); |
154 | | |
155 | | explicit ColumnWriter(TabletColumnPtr column, bool is_nullable, ColumnMetaPB* meta); |
156 | | |
157 | 23.7k | virtual ~ColumnWriter() = default; |
158 | | |
159 | | virtual Status init() = 0; |
160 | | |
161 | | // Write rows [row_pos, row_pos + num_rows) of `column`. |
162 | | virtual Status append(const IColumn& column, size_t row_pos, size_t num_rows) = 0; |
163 | | |
164 | | // Write num_rows NULL rows without any data. The variant writers use this to |
165 | | // pad a sub-column over rows where its path is absent. |
166 | | virtual Status append_nulls(size_t num_rows) = 0; |
167 | | |
168 | | virtual Status finish_current_page() = 0; |
169 | | |
170 | | virtual uint64_t estimate_buffer_size() = 0; |
171 | | |
172 | | // finish append data |
173 | | virtual Status finish() = 0; |
174 | | |
175 | | // write all data into file |
176 | | virtual Status write_data() = 0; |
177 | | |
178 | | virtual Status write_ordinal_index() = 0; |
179 | | |
180 | | virtual Status write_zone_map() = 0; |
181 | | |
182 | | virtual Status write_inverted_index() = 0; |
183 | | |
184 | 17.6k | virtual Status write_ann_index() { return Status::OK(); } |
185 | | |
186 | | virtual Status write_bloom_filter_index() = 0; |
187 | | |
188 | | virtual ordinal_t get_next_rowid() const = 0; |
189 | | |
190 | | virtual uint64_t get_raw_data_bytes() const = 0; |
191 | | virtual uint64_t get_total_uncompressed_data_pages_bytes() const = 0; |
192 | | virtual uint64_t get_total_compressed_data_pages_bytes() const = 0; |
193 | | // Points to one of the three data bytes getters above. |
194 | | using DataBytesGetter = uint64_t (ColumnWriter::*)() const; |
195 | | |
196 | 956k | bool is_nullable() const { return _is_nullable; } |
197 | | |
198 | 859k | const TabletColumn* get_column() const { return _column.get(); } |
199 | | |
200 | | // Per-row footprint of a storage cell of this writer's column, used to step across the |
201 | | // prepared cells in the append run loops. |
202 | 830k | size_t cell_size() const { return field_type_size(_column->type()); } |
203 | | |
204 | 35.3k | ColumnMetaPB* get_column_meta() const { return _column_meta; } |
205 | | |
206 | | private: |
207 | | TabletColumnPtr _column; |
208 | | bool _is_nullable; |
209 | | ColumnMetaPB* _column_meta; |
210 | | }; |
211 | | |
212 | | class FlushPageCallback { |
213 | | public: |
214 | 952 | virtual ~FlushPageCallback() = default; |
215 | 0 | virtual void put_extra_info_in_page(DataPageFooterPB* footer) {} |
216 | | }; |
217 | | |
218 | | // Writes scalar values and the offset/null streams of the composite writers. append cuts a |
219 | | // batch into its NULL runs, written to the null bitmap (append_nulls), and its value runs, |
220 | | // each converted to its storage cells (append_values) and written to the data pages and |
221 | | // every index (append_data). |
222 | | class ScalarColumnWriter : public ColumnWriter { |
223 | | public: |
224 | | ScalarColumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column, |
225 | | io::FileWriter* file_writer); |
226 | | |
227 | | ~ScalarColumnWriter() override; |
228 | | |
229 | | Status init() override; |
230 | | |
231 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
232 | | |
233 | | Status append_nulls(size_t num_rows) override; |
234 | | |
235 | | Status finish_current_page() override; |
236 | | |
237 | | uint64_t estimate_buffer_size() override; |
238 | | |
239 | | // finish append data |
240 | | Status finish() override; |
241 | | |
242 | | Status write_data() override; |
243 | | Status write_ordinal_index() override; |
244 | | Status write_zone_map() override; |
245 | | Status write_inverted_index() override; |
246 | | Status write_bloom_filter_index() override; |
247 | 3.63k | ordinal_t get_next_rowid() const override { return _next_rowid; } |
248 | | |
249 | 21.2k | uint64_t get_raw_data_bytes() const override { return _raw_data_bytes; } |
250 | | |
251 | 21.2k | uint64_t get_total_uncompressed_data_pages_bytes() const override { |
252 | 21.2k | return _total_uncompressed_data_pages_size; |
253 | 21.2k | } |
254 | | |
255 | 21.5k | uint64_t get_total_compressed_data_pages_bytes() const override { |
256 | 21.5k | return _total_compressed_data_pages_size; |
257 | 21.5k | } |
258 | | |
259 | 952 | void register_flush_page_callback(FlushPageCallback* flush_page_callback) { |
260 | 952 | _new_page_callback = flush_page_callback; |
261 | 952 | } |
262 | | |
263 | | protected: |
264 | | // used for append not null data. When page is full, will append data not reach num_rows. |
265 | 831k | Status append_data_in_current_page(const uint8_t* ptr, size_t* num_written) { |
266 | 831k | RETURN_IF_CATCH_EXCEPTION( |
267 | 831k | { return _internal_append_data_in_current_page(ptr, num_written); }); |
268 | 831k | } |
269 | | friend class OffsetColumnWriter; |
270 | | friend class NullMapColumnWriter; |
271 | | |
272 | | private: |
273 | | Status _internal_append_data_in_current_page(const uint8_t* ptr, size_t* num_written); |
274 | | |
275 | | // Converts rows [row_pos, row_pos + num_rows) of `values`, a run without NULL rows, to |
276 | | // num_rows cells of cell_size() bytes back to back through the StorageLayout of |
277 | | // _storage_type, borrowed from the column or held in _buffers until the next run, and |
278 | | // writes them. |
279 | | Status append_values(const IColumn& values, size_t row_pos, size_t num_rows); |
280 | | |
281 | | // Writes cells already in this column's storage layout, none of them NULL, page by page. |
282 | | // NullMapColumnWriter feeds a null map's flags, which are its TINYINT cells, directly. |
283 | | Status append_data(const uint8_t* cells, size_t num_rows); |
284 | | |
285 | | void _reserve_null_bitmap(const uint8_t* null_map, size_t num_rows); |
286 | | |
287 | | // The StorageLayout the values are stored through: the column's own type, or what an |
288 | | // AGG_STATE column's aggregate function serialises to. |
289 | | FieldType _storage_type; |
290 | | // Where append_values puts what it cannot borrow from the column. |
291 | | StorageBuffers _buffers; |
292 | | |
293 | | std::unique_ptr<PageBuilder> _page_builder; |
294 | | |
295 | | std::unique_ptr<NullBitmapBuilder> _null_bitmap_builder; |
296 | | |
297 | | ColumnWriterOptions _opts; |
298 | | |
299 | | const EncodingInfo* _encoding_info = nullptr; |
300 | | |
301 | | ordinal_t _next_rowid = 0; |
302 | | |
303 | | // All Pages will be organized into a linked list |
304 | | struct Page { |
305 | | // the data vector may contain: |
306 | | // 1. one OwnedSlice if the page body is compressed |
307 | | // 2. one OwnedSlice if the page body is not compressed and doesn't have nullmap |
308 | | // 3. two OwnedSlice if the page body is not compressed and has nullmap |
309 | | // use vector for easier management for lifetime of OwnedSlice |
310 | | std::vector<OwnedSlice> data; |
311 | | PageFooterPB footer; |
312 | | }; |
313 | | |
314 | 28.4k | void _push_back_page(std::unique_ptr<Page> page) { |
315 | 48.4k | for (auto& data_slice : page->data) { |
316 | 48.4k | _data_size += data_slice.slice().size; |
317 | 48.4k | } |
318 | | // estimate (page footer + footer size + checksum) took 20 bytes |
319 | 28.4k | _data_size += 20; |
320 | | // add page to pages' tail |
321 | 28.4k | _pages.emplace_back(std::move(page)); |
322 | 28.4k | } |
323 | | |
324 | | Status _write_data_page(Page* page); |
325 | | |
326 | | private: |
327 | | io::FileWriter* _file_writer = nullptr; |
328 | | // total size of data page list |
329 | | uint64_t _data_size; |
330 | | |
331 | | uint64_t _raw_data_bytes {0}; |
332 | | uint64_t _total_uncompressed_data_pages_size {0}; |
333 | | uint64_t _total_compressed_data_pages_size {0}; |
334 | | |
335 | | // cached generated pages, |
336 | | std::vector<std::unique_ptr<Page>> _pages; |
337 | | ordinal_t _first_rowid = 0; |
338 | | |
339 | | BlockCompressionCodec* _compress_codec; |
340 | | |
341 | | std::unique_ptr<OrdinalIndexWriter> _ordinal_index_builder; |
342 | | std::unique_ptr<ZoneMapIndexWriter> _zone_map_index_builder; |
343 | | std::vector<std::unique_ptr<IndexColumnWriter>> _inverted_index_builders; |
344 | | std::unique_ptr<BloomFilterIndexWriter> _bloom_filter_index_builder; |
345 | | |
346 | | // call before flush data page. |
347 | | FlushPageCallback* _new_page_callback = nullptr; |
348 | | }; |
349 | | |
350 | | // offsetColumnWriter is used column which has offset column, like array, map. |
351 | | // column type is only uint64 and should response for whole column value [start, end], end will set |
352 | | // in footer.next_array_item_ordinal which in finish_cur_page() callback put_extra_info_in_page() |
353 | | class OffsetColumnWriter final : public ScalarColumnWriter, FlushPageCallback { |
354 | | public: |
355 | | OffsetColumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column, |
356 | | io::FileWriter* file_writer); |
357 | | |
358 | | ~OffsetColumnWriter() override; |
359 | | |
360 | | Status init() override; |
361 | | |
362 | | // `column` holds the rebased offsets, one entry more than the rows. |
363 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
364 | | |
365 | | private: |
366 | | void put_extra_info_in_page(DataPageFooterPB* footer) override; |
367 | | |
368 | | uint64_t _next_offset; |
369 | | }; |
370 | | |
371 | | // The null map of a nullable STRUCT, ARRAY or MAP, stored as a NOT NULL TINYINT column. append |
372 | | // takes the composite column's null map column, whose flag bytes are the stored cells. |
373 | | class NullMapColumnWriter final : public ScalarColumnWriter { |
374 | | public: |
375 | | using ScalarColumnWriter::ScalarColumnWriter; |
376 | | |
377 | | Status append(const IColumn& null_map, size_t row_pos, size_t num_rows) override; |
378 | | }; |
379 | | |
380 | | // STRUCT values, including nested structs, with one child writer per field. |
381 | | class StructColumnWriter final : public ColumnWriter { |
382 | | public: |
383 | | explicit StructColumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column, |
384 | | NullMapColumnWriter* null_writer, |
385 | | std::vector<std::unique_ptr<ColumnWriter>>& sub_column_writers); |
386 | 39 | ~StructColumnWriter() override = default; |
387 | | |
388 | | Status init() override; |
389 | | |
390 | | // Recurse into each sub-column writer; write our own null bits. |
391 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
392 | | |
393 | | uint64_t estimate_buffer_size() override; |
394 | | |
395 | | Status finish() override; |
396 | | Status write_data() override; |
397 | | Status write_ordinal_index() override; |
398 | 0 | Status append_nulls(size_t num_rows) override { |
399 | 0 | return Status::NotSupported("struct writer can not append_nulls"); |
400 | 0 | } |
401 | | |
402 | | Status finish_current_page() override; |
403 | | |
404 | 38 | Status write_zone_map() override { |
405 | 38 | if (_opts.need_zone_map) { |
406 | 0 | return Status::NotSupported("struct not support zone map"); |
407 | 0 | } |
408 | 38 | return Status::OK(); |
409 | 38 | } |
410 | | |
411 | | Status write_inverted_index() override; |
412 | 38 | Status write_bloom_filter_index() override { |
413 | 38 | if (_opts.need_bloom_filter) { |
414 | 0 | return Status::NotSupported("struct not support bloom filter index"); |
415 | 0 | } |
416 | 38 | return Status::OK(); |
417 | 38 | } |
418 | | |
419 | 41 | ordinal_t get_next_rowid() const override { return _sub_column_writers[0]->get_next_rowid(); } |
420 | | |
421 | 38 | uint64_t get_raw_data_bytes() const override { |
422 | 38 | return _get_total_data_pages_bytes(&ColumnWriter::get_raw_data_bytes); |
423 | 38 | } |
424 | | |
425 | 38 | uint64_t get_total_uncompressed_data_pages_bytes() const override { |
426 | 38 | return _get_total_data_pages_bytes(&ColumnWriter::get_total_uncompressed_data_pages_bytes); |
427 | 38 | } |
428 | | |
429 | 38 | uint64_t get_total_compressed_data_pages_bytes() const override { |
430 | 38 | return _get_total_data_pages_bytes(&ColumnWriter::get_total_compressed_data_pages_bytes); |
431 | 38 | } |
432 | | |
433 | | private: |
434 | | template <typename Func> |
435 | 114 | uint64_t _get_total_data_pages_bytes(Func func) const { |
436 | 114 | uint64_t size = is_nullable() ? std::invoke(func, _null_writer.get()) : 0; |
437 | 324 | for (const auto& writer : _sub_column_writers) { |
438 | 324 | size += std::invoke(func, writer.get()); |
439 | 324 | } |
440 | 114 | return size; |
441 | 114 | } |
442 | | |
443 | | private: |
444 | | size_t _num_sub_column_writers; |
445 | | std::unique_ptr<NullMapColumnWriter> _null_writer; |
446 | | std::vector<std::unique_ptr<ColumnWriter>> _sub_column_writers; |
447 | | ColumnWriterOptions _opts; |
448 | | }; |
449 | | |
450 | | // ARRAY values and array-backed AGG_STATE values, with offset, null-map and item writers. |
451 | | class ArrayColumnWriter final : public ColumnWriter { |
452 | | public: |
453 | | explicit ArrayColumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column, |
454 | | OffsetColumnWriter* offset_writer, NullMapColumnWriter* null_writer, |
455 | | std::unique_ptr<ColumnWriter> item_writer); |
456 | 223 | ~ArrayColumnWriter() override = default; |
457 | | |
458 | | Status init() override; |
459 | | |
460 | | // Rebase the offsets to disk layout, recurse into _item_writer, feed the |
461 | | // array index writers. |
462 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
463 | | |
464 | | uint64_t estimate_buffer_size() override; |
465 | | |
466 | | Status finish() override; |
467 | | Status write_data() override; |
468 | | Status write_ordinal_index() override; |
469 | | Status append_nulls(size_t num_rows) override; |
470 | | |
471 | | Status finish_current_page() override; |
472 | | |
473 | 98 | Status write_zone_map() override { |
474 | 98 | if (_opts.need_zone_map) { |
475 | 0 | return Status::NotSupported("array not support zone map"); |
476 | 0 | } |
477 | 98 | return Status::OK(); |
478 | 98 | } |
479 | | |
480 | | Status write_inverted_index() override; |
481 | | Status write_ann_index() override; |
482 | 98 | Status write_bloom_filter_index() override { |
483 | 98 | if (_opts.need_bloom_filter) { |
484 | 0 | return Status::NotSupported("array not support bloom filter index"); |
485 | 0 | } |
486 | 98 | return Status::OK(); |
487 | 98 | } |
488 | 227 | ordinal_t get_next_rowid() const override { return _offset_writer->get_next_rowid(); } |
489 | | |
490 | 197 | uint64_t get_raw_data_bytes() const override { |
491 | 197 | return _get_total_data_pages_bytes(&ColumnWriter::get_raw_data_bytes); |
492 | 197 | } |
493 | | |
494 | 197 | uint64_t get_total_uncompressed_data_pages_bytes() const override { |
495 | 197 | return _get_total_data_pages_bytes(&ColumnWriter::get_total_uncompressed_data_pages_bytes); |
496 | 197 | } |
497 | | |
498 | 205 | uint64_t get_total_compressed_data_pages_bytes() const override { |
499 | 205 | return _get_total_data_pages_bytes(&ColumnWriter::get_total_compressed_data_pages_bytes); |
500 | 205 | } |
501 | | |
502 | | private: |
503 | | template <typename Func> |
504 | 599 | uint64_t _get_total_data_pages_bytes(Func func) const { |
505 | 599 | uint64_t size = std::invoke(func, _offset_writer.get()); |
506 | 599 | if (is_nullable()) { |
507 | 386 | size += std::invoke(func, _null_writer.get()); |
508 | 386 | } |
509 | 599 | size += std::invoke(func, _item_writer.get()); |
510 | 599 | return size; |
511 | 599 | } |
512 | | |
513 | | private: |
514 | 215 | bool has_empty_items() const { return _item_writer->get_next_rowid() == 0; } |
515 | | |
516 | | private: |
517 | | std::unique_ptr<OffsetColumnWriter> _offset_writer; |
518 | | std::unique_ptr<NullMapColumnWriter> _null_writer; |
519 | | std::unique_ptr<ColumnWriter> _item_writer; |
520 | | std::unique_ptr<IndexColumnWriter> _inverted_index_writer; |
521 | | std::unique_ptr<AnnIndexColumnWriter> _ann_index_writer; |
522 | | ColumnWriterOptions _opts; |
523 | | // The current batch's offsets as the offset writer and the index writers |
524 | | // read them. |
525 | | ColumnOffset64::MutablePtr _offsets_buffer = ColumnOffset64::create(); |
526 | | // Storage cells for array indexes, independent of the element writer's page buffers. |
527 | | StorageBuffers _index_buffers; |
528 | | }; |
529 | | |
530 | | // MAP values and map-backed AGG_STATE values, with offset, null-map, key and value writers. |
531 | | class MapColumnWriter final : public ColumnWriter { |
532 | | public: |
533 | | explicit MapColumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column, |
534 | | NullMapColumnWriter* null_writer, OffsetColumnWriter* offsets_writer, |
535 | | std::vector<std::unique_ptr<ColumnWriter>>& _kv_writers); |
536 | | |
537 | 727 | ~MapColumnWriter() override = default; |
538 | | |
539 | | Status init() override; |
540 | | |
541 | | // Rebase the offsets to disk layout, recurse into the key and value writers. |
542 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
543 | | |
544 | | uint64_t estimate_buffer_size() override; |
545 | | |
546 | | Status finish() override; |
547 | | Status write_data() override; |
548 | | Status write_ordinal_index() override; |
549 | | Status write_inverted_index() override; |
550 | 0 | Status append_nulls(size_t num_rows) override { |
551 | 0 | return Status::NotSupported("map writer can not append_nulls"); |
552 | 0 | } |
553 | | |
554 | | Status finish_current_page() override; |
555 | | |
556 | 114 | Status write_zone_map() override { |
557 | 114 | if (_opts.need_zone_map) { |
558 | 0 | return Status::NotSupported("map not support zone map"); |
559 | 0 | } |
560 | 114 | return Status::OK(); |
561 | 114 | } |
562 | | |
563 | 114 | Status write_bloom_filter_index() override { |
564 | 114 | if (_opts.need_bloom_filter) { |
565 | 0 | return Status::NotSupported("map not support bloom filter index"); |
566 | 0 | } |
567 | 114 | return Status::OK(); |
568 | 114 | } |
569 | | |
570 | | // according key writer to get next rowid |
571 | 724 | ordinal_t get_next_rowid() const override { return _offsets_writer->get_next_rowid(); } |
572 | | |
573 | 601 | uint64_t get_raw_data_bytes() const override { |
574 | 601 | return _get_total_data_pages_bytes(&ColumnWriter::get_raw_data_bytes); |
575 | 601 | } |
576 | | |
577 | 601 | uint64_t get_total_uncompressed_data_pages_bytes() const override { |
578 | 601 | return _get_total_data_pages_bytes(&ColumnWriter::get_total_uncompressed_data_pages_bytes); |
579 | 601 | } |
580 | | |
581 | 685 | uint64_t get_total_compressed_data_pages_bytes() const override { |
582 | 685 | return _get_total_data_pages_bytes(&ColumnWriter::get_total_compressed_data_pages_bytes); |
583 | 685 | } |
584 | | |
585 | | private: |
586 | | template <typename Func> |
587 | 1.88k | uint64_t _get_total_data_pages_bytes(Func func) const { |
588 | 1.88k | uint64_t size = std::invoke(func, _offsets_writer.get()); |
589 | 1.88k | if (is_nullable()) { |
590 | 63 | size += std::invoke(func, _null_writer.get()); |
591 | 63 | } |
592 | 3.77k | for (const auto& writer : _kv_writers) { |
593 | 3.77k | size += std::invoke(func, writer.get()); |
594 | 3.77k | } |
595 | 1.88k | return size; |
596 | 1.88k | } |
597 | | |
598 | | private: |
599 | | std::vector<std::unique_ptr<ColumnWriter>> _kv_writers; |
600 | | // we need null writer to make sure a row is null or not |
601 | | std::unique_ptr<NullMapColumnWriter> _null_writer; |
602 | | std::unique_ptr<OffsetColumnWriter> _offsets_writer; |
603 | | std::unique_ptr<IndexColumnWriter> _index_builder; |
604 | | ColumnWriterOptions _opts; |
605 | | ColumnOffset64::MutablePtr _map_offsets_buffer = ColumnOffset64::create(); |
606 | | }; |
607 | | |
608 | | // Extracted VARIANT subcolumns. |
609 | | class VariantSubcolumnWriter : public ColumnWriter { |
610 | | public: |
611 | | explicit VariantSubcolumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column); |
612 | | |
613 | | ~VariantSubcolumnWriter() override; |
614 | | |
615 | | Status init() override; |
616 | | |
617 | | uint64_t estimate_buffer_size() override; |
618 | | |
619 | | Status finish() override; |
620 | | Status write_data() override; |
621 | | Status write_ordinal_index() override; |
622 | | |
623 | | Status write_zone_map() override; |
624 | | |
625 | | Status write_inverted_index() override; |
626 | | Status write_bloom_filter_index() override; |
627 | 2 | ordinal_t get_next_rowid() const override { return _next_rowid; } |
628 | | |
629 | 10 | uint64_t get_raw_data_bytes() const override { return _writer->get_raw_data_bytes(); } |
630 | | |
631 | 10 | uint64_t get_total_uncompressed_data_pages_bytes() const override { |
632 | 10 | return _writer->get_total_uncompressed_data_pages_bytes(); |
633 | 10 | } |
634 | | |
635 | 39 | uint64_t get_total_compressed_data_pages_bytes() const override { |
636 | 39 | return _writer->get_total_compressed_data_pages_bytes(); |
637 | 39 | } |
638 | | |
639 | 0 | Status append_nulls(size_t num_rows) override { |
640 | 0 | return Status::NotSupported("variant writer can not append_nulls"); |
641 | 0 | } |
642 | | |
643 | | // Splits off the null map and hands the nested column down to the |
644 | | // variant writers. |
645 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
646 | | |
647 | 0 | Status finish_current_page() override { |
648 | 0 | return Status::NotSupported("variant writer has no data, can not finish_current_page"); |
649 | 0 | } |
650 | | |
651 | 0 | size_t get_non_null_size() const { return none_null_size; } |
652 | | |
653 | | #ifdef BE_TEST |
654 | | // Force small chunk budgets so tests can exercise multi-chunk flushes cheaply. |
655 | | static void set_chunk_bytes_for_test(size_t bytes); |
656 | | static void set_chunk_values_for_test(size_t values); |
657 | | size_t num_chunks_for_test() const; |
658 | | size_t max_chunk_bytes_for_test() const; |
659 | | #endif |
660 | | |
661 | | Status finalize(); |
662 | | |
663 | | private: |
664 | | Status _append(const IColumn& column, size_t row_pos, size_t num_rows, const uint8_t* null_map); |
665 | | Status _append_v2(const IColumn& column, size_t row_pos, size_t num_rows, |
666 | | std::span<const uint8_t> outer_nulls); |
667 | | Status _ensure_input_format(const IColumn& column); |
668 | | Status _initialize_v2_builder(); |
669 | | Status _promote_chunks(DataTypePtr* inferred_type); |
670 | | Status _seal_chunk(); |
671 | | // Bounded value chunks; each chunk is finalized and flushed independently so the writer never |
672 | | // holds a segment-sized ColumnString. |
673 | | struct ValueChunk { |
674 | | std::unique_ptr<VariantPathBuilder> builder; |
675 | | size_t row_begin = 0; |
676 | | size_t rows = 0; |
677 | | }; |
678 | | static constexpr size_t kChunkBytes = 256ULL << 20; |
679 | | static constexpr size_t kChunkValues = 4ULL << 20; |
680 | | bool is_finalized() const; |
681 | | bool _is_finalized = false; |
682 | | ordinal_t _next_rowid = 0; |
683 | | size_t none_null_size = 0; |
684 | | VariantWriterInputFormat _input_format = VariantWriterInputFormat::UNSET; |
685 | | std::unique_ptr<VariantPathBuilder> _v2_builder; |
686 | | std::vector<ValueChunk> _chunks; |
687 | | size_t _chunk_row_begin = 0; |
688 | | size_t _chunk_rows = 0; |
689 | | // Type carried into the next chunk. Inferred promotions also check and update sealed chunks |
690 | | // so fallback follows the same sequence as one uninterrupted builder. |
691 | | DataTypePtr _running_type; |
692 | | size_t _num_rows = 0; |
693 | | ColumnWriterOptions _opts; |
694 | | std::unique_ptr<ColumnWriter> _writer; |
695 | | TabletIndexes _indexes; |
696 | | |
697 | | std::unique_ptr<NestedGroupWriteProvider> _nested_group_provider; |
698 | | VariantStatistics _statistics; |
699 | | }; |
700 | | |
701 | | // Root VARIANT values and their physical subcolumns. |
702 | | class VariantColumnWriter : public ColumnWriter { |
703 | | public: |
704 | | explicit VariantColumnWriter(const ColumnWriterOptions& opts, TabletColumnPtr column); |
705 | | |
706 | 558 | ~VariantColumnWriter() override = default; |
707 | | |
708 | | Status init() override; |
709 | | |
710 | | uint64_t estimate_buffer_size() override; |
711 | | |
712 | | Status finish() override; |
713 | | Status write_data() override; |
714 | | Status write_ordinal_index() override; |
715 | | |
716 | | Status write_zone_map() override; |
717 | | |
718 | | Status write_inverted_index() override; |
719 | | Status write_bloom_filter_index() override; |
720 | 1 | ordinal_t get_next_rowid() const override { return _next_rowid; } |
721 | | |
722 | | uint64_t get_raw_data_bytes() const override; |
723 | | |
724 | | uint64_t get_total_uncompressed_data_pages_bytes() const override; |
725 | | |
726 | | uint64_t get_total_compressed_data_pages_bytes() const override; |
727 | | |
728 | 0 | Status append_nulls(size_t num_rows) override { |
729 | 0 | return Status::NotSupported("variant writer can not append_nulls"); |
730 | 0 | } |
731 | | |
732 | | // Splits off the null map and hands the nested column down to the |
733 | | // variant writers. |
734 | | Status append(const IColumn& column, size_t row_pos, size_t num_rows) override; |
735 | | |
736 | 0 | Status finish_current_page() override { |
737 | 0 | return Status::NotSupported("variant writer has no data, can not finish_current_page"); |
738 | 0 | } |
739 | | |
740 | 0 | VariantColumnWriterImpl* impl_for_test() const { return _impl.get(); } |
741 | | |
742 | | private: |
743 | | std::unique_ptr<VariantColumnWriterImpl> _impl; |
744 | | ordinal_t _next_rowid = 0; |
745 | | }; |
746 | | |
747 | | } // namespace segment_v2 |
748 | | } // namespace doris |