Skip to content

Commit dba37ec

Browse files
committed
update files
Signed-off-by: tqqq <dirtysalt1987@gmail.com>
1 parent 799d052 commit dba37ec

5 files changed

Lines changed: 38 additions & 39 deletions

File tree

be/src/formats/parquet/column_materializer.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,13 @@ class ColumnMaterializer {
229229
// 1. read_active_range_round_by_round()
230230
// - read_slot() reads active columns → PHYSICAL in _read_chunk
231231
// - Dict filters run ON physical columns
232-
// - Post-read conjuncts → finalize_lazy_state() → LOGICAL in chunk
232+
// - Post-read conjuncts → finalize_active_slot() → LOGICAL + marks
233+
// _logical_slot_ids so emit bypasses fill_dst_column
233234
//
234-
// 2. Compound predicate eval (ChunkPredicateEvaluator)
235+
// 2. Compound predicate eval (GroupReader Phase 2b)
236+
// - finalize_active_slot() on all active columns (also marks
237+
// _logical_slot_ids)
235238
// - ColumnRef for missing lazy slots calls MissingColumnProvider
236-
// → LazyMaterializationContext::provide()
237239
// → materialize_slot(): read_range() + finalize_lazy_state()
238240
// → column in _read_chunk is LOGICAL, cached in _slot_cache
239241
//

be/src/formats/parquet/group_reader.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,6 @@ Status GroupReader::get_next(ChunkPtr* chunk, size_t* row_count) {
197197
return Status::EndOfFile("");
198198
}
199199

200-
ChunkPtr active_chunk = _column_materializer->create_active_chunk();
201-
202200
while (true) {
203201
if (!_range_iter.has_more()) {
204202
*row_count = 0;
@@ -209,13 +207,19 @@ Status GroupReader::get_next(ChunkPtr* chunk, size_t* row_count) {
209207
auto count = r.span_size();
210208
_param.stats->raw_rows_read += count;
211209

212-
// Previous iteration's triggered-lazy slot cache must not carry over into
213-
// the next range — stale slot_cache entries would corrupt read_lazy_columns.
210+
// Per-range state reset. _slot_cache and _logical_slot_ids are
211+
// cleared here; _fetched_hidden_slots is cleared by reset_iteration_state.
214212
_column_materializer->reset_read_chunk();
215213
_variant->reset_iteration_state();
214+
215+
// Rebuild active_chunk per range so that slots appended during
216+
// predicate evaluation / lazy backfill / variant projection in a
217+
// prior range do not leak into the current range. Chunk::reset()
218+
// preserves slot mappings which can leave stale empty columns.
219+
ChunkPtr active_chunk = _column_materializer->create_active_chunk();
220+
216221
bool has_filter = false;
217222
Filter chunk_filter(count, 1);
218-
active_chunk->reset();
219223

220224
// Phase 4: lazy materialization context for on-demand slot resolution.
221225
// Created early so Phase 6 expression trigger can call materialize_slot()
@@ -260,11 +264,9 @@ Status GroupReader::get_next(ChunkPtr* chunk, size_t* row_count) {
260264
// variant virtual slots are created by emit_projections() which
261265
// runs later. When a compound conjunct references such a slot,
262266
// we fetch only the needed hidden sources and project only the
263-
// needed virtual slots early. The projected slots stay in
264-
// active_chunk through filtering and are reused by
265-
// emit_projections() at Phase 7. Any remaining hidden sources
266-
// are fetched by the normal Phase 3 call — fetch_sources()
267-
// skips already-populated slots via _fetched_hidden_slots.
267+
// needed virtual slots early. Because active_chunk is rebuilt
268+
// per range, there is no risk of stale projected slots from a
269+
// prior range that was fully filtered.
268270
std::unordered_set<SlotId> early_projected_slots;
269271
if (!_param.scanner_ctx->conjuncts.scanner_ctxs.empty()) {
270272
if (!_variant->empty()) {

be/src/formats/parquet/lazy_materialization_context.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,10 @@ bool LazyMaterializationContext::can_materialize(SlotId slot_id) const {
4242
}
4343

4444
Status LazyMaterializationContext::materialize_slot(SlotId slot_id) {
45-
// Variant lazy hidden source slot: dispatch to VariantProjectionHandler
46-
// BEFORE checking has_slot(). Chunk::reset() preserves slot indices
47-
// from prior iterations that were fully filtered, so has_slot() returns
48-
// true for stale empty columns. materialize_hidden_source() handles
49-
// the stale case by re-reading into update_column.
45+
// Variant lazy hidden sources are dispatched to VariantProjectionHandler
46+
// before checking has_slot(), because they live outside the regular
47+
// slot_cache / slot_id_index. materialize_hidden_source() own dedup
48+
// via _fetched_hidden_slots.
5049
if (_variant != nullptr) {
5150
const auto& hidden_lazy = _variant->lazy_hidden_slot_ids();
5251
if (std::find(hidden_lazy.begin(), hidden_lazy.end(), slot_id) != hidden_lazy.end()) {

be/src/formats/parquet/variant_projection.cpp

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -800,10 +800,9 @@ Status VariantProjectionHandler::fetch_and_project_virtual_slots(const std::unor
800800
}
801801

802802
// 2. Project each needed virtual slot from its source column.
803-
// Re-project every iteration — Chunk::reset() clears column data
804-
// but preserves slot indices (is_slot_exist stays true), so a
805-
// prior range that was fully filtered would leave an empty stale
806-
// column that skipping would reuse without fresh projection.
803+
// active_chunk is rebuilt per range, so append_column is the
804+
// common case. When called multiple times within the same
805+
// iteration update_column replaces a previously projected slot.
807806
for (SlotId virtual_slot_id : virtual_slot_ids) {
808807
auto proj_it = _projections.find(virtual_slot_id);
809808
DCHECK(proj_it != _projections.end());
@@ -909,11 +908,9 @@ Status VariantProjectionHandler::backfill_sources(const Range<uint64_t>& full_ra
909908

910909
auto lazy_hidden_chunk = std::make_shared<Chunk>();
911910
for (SlotId slot_id : _lazy_hidden_slot_ids) {
912-
// Use _fetched_hidden_slots, not active_chunk->is_slot_exist().
913-
// Chunk::reset() preserves slot indices from a prior iteration that
914-
// was fully filtered, so is_slot_exist() would return true for stale
915-
// empty columns. _fetched_hidden_slots tracks what was actually
916-
// populated THIS iteration.
911+
// Skip slots already populated this iteration via on-demand
912+
// materialization (materialize_hidden_source) or early fetch
913+
// (fetch_and_project_virtual_slots).
917914
if (_fetched_hidden_slots.count(slot_id)) continue;
918915

919916
auto* hidden_src = _hidden_slot_index.at(slot_id);
@@ -946,23 +943,21 @@ Status VariantProjectionHandler::materialize_hidden_source(SlotId slot_id, const
946943
if (std::find(lazy_ids.begin(), lazy_ids.end(), slot_id) == lazy_ids.end()) {
947944
return Status::OK();
948945
}
949-
// Chunk::reset() preserves slot indices but clears data. A slot may
950-
// exist from a prior iteration that was fully filtered. Re-read the
951-
// source in every case — update_column replaces stale data, append_column
952-
// is used only on the first population.
946+
// Already populated this iteration (by an earlier on-demand trigger).
947+
if (_fetched_hidden_slots.count(slot_id)) {
948+
return Status::OK();
949+
}
953950
auto it = _hidden_slot_index.find(slot_id);
954951
if (it == _hidden_slot_index.end()) {
955952
return Status::InternalError(
956953
fmt::format("materialize_hidden_source: slot {} not in hidden_slot_index", slot_id));
957954
}
955+
// active_chunk is rebuilt per range, so the slot never exists from a
956+
// prior iteration — always append.
958957
ColumnPtr col = ColumnHelper::create_column(TypeDescriptor::from_logical_type(TYPE_VARIANT), true);
959958
RETURN_IF_ERROR(it->second->reader->read_range(range, filter, col));
960959
RETURN_IF_ERROR(it->second->reader->finalize_lazy_state(col));
961-
if (active_chunk->is_slot_exist(slot_id)) {
962-
active_chunk->update_column(std::move(col), slot_id);
963-
} else {
964-
active_chunk->append_column(std::move(col), slot_id);
965-
}
960+
active_chunk->append_column(std::move(col), slot_id);
966961
_fetched_hidden_slots.insert(slot_id);
967962
return Status::OK();
968963
}

be/src/formats/parquet/variant_projection.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,15 @@ class VariantProjectionHandler {
131131
Status align_after_combined_filter(const ChunkPtr& active_chunk, const Filter& filter, size_t pre_filter_rows);
132132

133133
// 6. Backfill projection-only variant source columns.
134-
// Slots already triggered via LazyMaterializationContext (already present in
135-
// active_chunk) are skipped to avoid double-reading.
134+
// Slots already populated this iteration (tracked by _fetched_hidden_slots)
135+
// are skipped to avoid double-reading.
136136
Status backfill_sources(const Range<uint64_t>& full_range, const Range<uint64_t>* post_filter_range,
137137
const Filter* post_filter, bool has_filter, ChunkPtr& active_chunk);
138138

139139
// On-demand materialization of a single lazy hidden source slot into active_chunk.
140140
// Called by LazyMaterializationContext when an expression triggers the slot.
141-
// No-op if slot_id is already in active_chunk or is not a lazy hidden source.
141+
// No-op if slot_id is not a lazy hidden source or was already populated this
142+
// iteration (tracked by _fetched_hidden_slots).
142143
Status materialize_hidden_source(SlotId slot_id, const Range<uint64_t>& range, const Filter* filter,
143144
ChunkPtr& active_chunk);
144145

0 commit comments

Comments
 (0)