blob: 61ed83c44ce0715a302c07107d9a60b5387995aa [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <memory>
#include <string_view>
#include <utility>
#include <vector>
#include "common/status.h"
#include "storage/olap_common.h"
#include "storage/olap_define.h"
#include "storage/partial_update_info.h"
#include "storage/tablet/tablet_fwd.h"
namespace doris {
class Block;
class IColumn;
struct RowsetWriterContext;
namespace segment_v2 {
// Computes a derived column (e.g. the hidden row-store column) in bounded batches.
class DerivedColumnGenerator {
public:
virtual ~DerivedColumnGenerator() = default;
// Appends a bounded batch (<= max_rows, stops near max_bytes, but always >= 1
// row); returns the rows produced. A generator may snapshot its source rows at
// registration time, so it must only be driven with the block it was built over:
// `block` and `row_pos` index the same rows the snapshot holds.
virtual size_t generate(const Block& block, size_t row_pos, size_t max_rows, size_t max_bytes,
IColumn* dst) const = 0;
};
using DerivedColumn = std::pair<uint32_t, std::shared_ptr<const DerivedColumnGenerator>>;
// For the horizontal writer, which can't feed derived columns in bounded batches.
Status materialize_derived_columns(const DerivedColumn& derived_column, Block* block);
// State for each flush, kept out of the chain so the chain stays immutable and
// shareable across concurrent flushes. Filled in for each flush; the tablet
// schema in particular differs between flushes for variant tables.
struct TransformExecContext {
TabletSchemaSPtr tablet_schema;
DataWriteType write_type = DataWriteType::TYPE_DEFAULT;
// --- partial update inputs (filled by the call sites from RowsetWriterContext) ---
BaseTabletSPtr tablet;
std::shared_ptr<MowContext> mow_context;
std::shared_ptr<PartialUpdateInfo> partial_update_info;
RowsetWriterContext* rowset_ctx = nullptr;
// identifies the segment this block lands in (for the self-marks)
RowsetId rowset_id;
// -1 until flush_single_block sets it; partial update needs it set.
int32_t segment_id = -1;
// --- outputs ---
// the derived column for the writer to generate in batches; null generator = none
DerivedColumn derived_column;
// probe counters of the partial-update fill stages; the flush seam folds them
// into the flusher totals
PartialUpdateStats partial_update_stats;
};
// One Block -> Block step run before the segment writers. apply() may mutate
// the block in place or swap its columns, but must not keep references to it.
class BlockTransform {
public:
virtual ~BlockTransform() = default;
virtual Status apply(TransformExecContext& ctx, Block* block) const = 0;
// Stable name used for debugging and to check how the chain is built.
virtual std::string_view name() const = 0;
};
// An ordered list of transforms. Building one is cheap, so the seams build it
// for each transformed block; it is immutable and shareable across concurrent
// flushes.
class BlockTransformChain {
public:
BlockTransformChain() = default;
explicit BlockTransformChain(std::vector<std::shared_ptr<const BlockTransform>> stages)
: _stages(std::move(stages)) {}
Status apply(TransformExecContext& ctx, Block* block) const {
for (const auto& stage : _stages) {
RETURN_IF_ERROR(stage->apply(ctx, block));
}
return Status::OK();
}
bool empty() const { return _stages.empty(); }
std::vector<std::string_view> stage_names() const {
std::vector<std::string_view> names;
names.reserve(_stages.size());
for (const auto& stage : _stages) {
names.push_back(stage->name());
}
return names;
}
private:
std::vector<std::shared_ptr<const BlockTransform>> _stages;
};
// The single place that decides which transforms a write path gets:
// - compaction: empty (rows are already final)
// - binlog<row> sub-writer: [PlainRowBinlogDerive] or [MowRowBinlogDerive]
// for a direct write, empty otherwise (rows are already binlog shaped)
// - fixed partial update: [Validate, FixedPartialUpdateFill, VariantParse, RowStoreFill]
// - flexible partial update: [Validate, FlexiblePartialUpdateFill, RowStoreFill, VariantParse]
// (row store before parse, the reverse of fixed: each order mirrors its legacy path)
// - direct / schema change / transient flush: [Validate, RowStoreFill, VariantParse]
// RowStoreFill is omitted when the write type does not rebuild the row-store column.
BlockTransformChain build_transform_chain(const RowsetWriterContext& context);
} // namespace segment_v2
} // namespace doris