-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[fix](parquet) Restrict V2 dictionary filtering to strings #66343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1271,6 +1271,9 @@ Status ScalarColumnReader<IN_COLLECTION, OFFSET_INDEX>::read_fixed_width_filter( | |
| ColumnChunkReader<IN_COLLECTION, OFFSET_INDEX>:: | ||
| supports_raw_binary_filter_encoding( | ||
| encoding, _chunk_meta.meta_data.type) || | ||
| ColumnChunkReader<IN_COLLECTION, OFFSET_INDEX>:: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] This chunk-level whitelist is broader than the page-level predicate capability. For a logical string backed by |
||
| supports_dictionary_fixed_filter_encoding( | ||
| encoding, _chunk_meta.meta_data.type) || | ||
| encoding == tparquet::Encoding::RLE || | ||
| encoding == tparquet::Encoding::BIT_PACKED; | ||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,77 @@ class FixLengthDictDecoder final : public BaseDictDecoder { | |
| static_cast<size_t>(_type_length)); | ||
| } | ||
|
|
||
| Status decode_selected_fixed_values(const ParquetSelection& selection, | ||
| ParquetFixedValueConsumer& consumer) override { | ||
| DORIS_CHECK_GT(_type_length, 0); | ||
| // Raw predicates on non-string dictionaries must observe decoded physical values, not | ||
| // dictionary IDs, so expand each validated index before invoking the predicate consumer. | ||
| class ExpandedValueConsumer final : public ParquetDictionaryValueConsumer { | ||
| public: | ||
| ExpandedValueConsumer(const uint8_t* dictionary, size_t dictionary_size, | ||
| size_t value_width, ParquetFixedValueConsumer& consumer, | ||
| std::vector<uint8_t>& scratch) | ||
| : _dictionary(dictionary), | ||
| _dictionary_size(dictionary_size), | ||
| _value_width(value_width), | ||
| _consumer(consumer), | ||
| _scratch(scratch) {} | ||
|
|
||
| Status consume_indices(const uint32_t* indices, size_t num_values) override { | ||
| DORIS_CHECK(indices != nullptr || num_values == 0); | ||
| if (UNLIKELY(num_values > std::numeric_limits<size_t>::max() / _value_width)) { | ||
| return Status::IOError("Parquet dictionary expansion size overflows"); | ||
| } | ||
| _scratch.resize(num_values * _value_width); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] This expansion is bounded by values, not bytes. |
||
| for (size_t row = 0; row < num_values; ++row) { | ||
| DORIS_CHECK_LT(indices[row], _dictionary_size); | ||
| memcpy(_scratch.data() + row * _value_width, | ||
| _dictionary + static_cast<size_t>(indices[row]) * _value_width, | ||
| _value_width); | ||
| } | ||
| return _consumer.consume(_scratch.data(), num_values, _value_width); | ||
| } | ||
|
|
||
| Status consume_repeated(uint32_t index, size_t num_values) override { | ||
| DORIS_CHECK_LT(index, _dictionary_size); | ||
| constexpr size_t MAX_BATCH_VALUES = 1024; | ||
| const uint8_t* value = _dictionary + static_cast<size_t>(index) * _value_width; | ||
| while (num_values > 0) { | ||
| const size_t batch = std::min(num_values, MAX_BATCH_VALUES); | ||
| _scratch.resize(batch * _value_width); | ||
| for (size_t row = 0; row < batch; ++row) { | ||
| memcpy(_scratch.data() + row * _value_width, value, _value_width); | ||
| } | ||
| RETURN_IF_ERROR(_consumer.consume(_scratch.data(), batch, _value_width)); | ||
| num_values -= batch; | ||
| } | ||
| return Status::OK(); | ||
| } | ||
|
|
||
| private: | ||
| const uint8_t* const _dictionary; | ||
| const size_t _dictionary_size; | ||
| const size_t _value_width; | ||
| ParquetFixedValueConsumer& _consumer; | ||
| std::vector<uint8_t>& _scratch; | ||
| } expanded_consumer(_dict.get(), _num_dictionary_values, static_cast<size_t>(_type_length), | ||
| consumer, _expanded_values); | ||
| return decode_selected_dictionary_values(selection, expanded_consumer); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] This streams directly into |
||
| } | ||
|
|
||
| void release_scratch(size_t max_retained_bytes) override { | ||
| BaseDictDecoder::release_scratch(max_retained_bytes); | ||
| release_vector_if_oversized(&_expanded_values, max_retained_bytes); | ||
| } | ||
|
|
||
| size_t retained_scratch_bytes() const override { | ||
| return BaseDictDecoder::retained_scratch_bytes() + _expanded_values.capacity(); | ||
| } | ||
|
|
||
| size_t active_scratch_bytes() const override { | ||
| return BaseDictDecoder::active_scratch_bytes() + _expanded_values.size(); | ||
| } | ||
|
|
||
| Status set_dict(DorisUniqueBufferPtr<uint8_t>& dict, int32_t length, | ||
| size_t num_values) override { | ||
| if (UNLIKELY(_type_length <= 0 || length < 0 || | ||
|
|
@@ -59,6 +130,7 @@ class FixLengthDictDecoder final : public BaseDictDecoder { | |
|
|
||
| private: | ||
| size_t _num_dictionary_values = 0; | ||
| std::vector<uint8_t> _expanded_values; | ||
| }; | ||
|
|
||
| } // namespace doris::format::parquet::native | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This guard rejects every primitive handled by
get_typed_dictionary_raw_values()beforebuild_dictionary_entry_filter()runs. As a result,TYPED_FIXED_WIDTH, the fixed-width fused projection path,DictFilterTypedCompareColumns, andDictionaryPredicateFusedProjectedRowsnow have no reachable producer, while the design metrics table and INT64 benchmark matrix still advertise them. Please remove/update the dead machinery and counters, or retain a semantics-safe reachable producer, so Profiles and benchmark coverage describe executable behavior.