blob: c8432efb4d0edcd52c0606346b790344e205dc8e [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 <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "common/status.h"
#include "core/column/column.h"
#include "core/data_type/data_type.h"
#include "storage/segment/column_reader.h"
#include "storage/segment/variant/nested_group_builder.h"
#include "storage/segment/variant/nested_group_path.h"
#include "storage/segment/variant/nested_group_reader.h"
#include "storage/segment/variant/nested_group_routing_plan.h"
#include "storage/segment/variant/nested_group_streaming_write_plan.h"
#include "util/json/path_in_data.h"
namespace roaring {
class Roaring;
}
namespace doris {
class TabletColumn;
class TabletSchema;
class StorageReadOptions;
namespace io {
class FileReader;
} // namespace io
class ColumnVariant;
class OlapBlockDataConvertor;
} // namespace doris
namespace doris::segment_v2 {
struct ColumnWriterOptions;
struct ColumnReaderOptions;
struct VariantStatistics;
struct NestedGroupReader;
class ColumnMetaAccessor;
class SegmentFooterPB;
// Path filter for selecting specific children in NestedGroup reads.
// Used to prune unnecessary child column I/O during query execution.
struct NestedGroupPathFilter {
bool allow_all = false;
std::unordered_set<std::string> allowed_paths;
bool empty() const { return !allow_all && allowed_paths.empty(); }
void set_allow_all() {
allow_all = true;
allowed_paths.clear();
}
void add_path(std::string path) {
if (!path.empty()) {
allowed_paths.emplace(std::move(path));
}
}
bool matches_child(const std::string& name) const {
if (allow_all) {
return true;
}
return std::ranges::any_of(allowed_paths, [&](const auto& path) {
return nested_group_paths_overlap(path, name);
});
}
NestedGroupPathFilter sub_filter(const std::string& prefix) const {
NestedGroupPathFilter sub;
if (allow_all) {
sub.allow_all = true;
return sub;
}
std::string prefix_dot = prefix + ".";
for (const auto& path : allowed_paths) {
if (path == prefix) {
sub.set_allow_all();
return sub;
}
if (path.starts_with(prefix_dot)) {
sub.add_path(path.substr(prefix_dot.size()));
}
}
return sub;
}
};
// Map from array path to NestedGroupReader.
// Defined here so both provider and reader can use it.
using NestedGroupReaders = std::unordered_map<std::string, std::unique_ptr<NestedGroupReader>>;
struct NestedGroupPathMatch {
const NestedGroupReader* reader = nullptr;
std::vector<const NestedGroupReader*> chain;
std::string child_path;
bool found = false;
};
// Shared resolver for nested-group path matching used by both reader and provider.
NestedGroupPathMatch find_in_nested_groups(const NestedGroupReaders& readers,
const std::string& path, bool collect_chain);
// --------------------------------------------------------------------------
// Write provider
// --------------------------------------------------------------------------
// Extension point for NestedGroup write path.
// Concrete behavior is selected by create_nested_group_write_provider().
// The default provider is a no-op placeholder.
// Downstream integrations may provide a full implementation that expands JSONB
// into NestedGroup columns with auxiliary indexes.
Status build_nested_groups_from_variant_jsonb(
const ColumnVariant& variant, NestedGroupsMap* nested_groups,
std::vector<std::string>* out_ng_paths = nullptr,
std::vector<std::string>* out_conflict_paths = nullptr);
class NestedGroupWriteProvider {
public:
virtual ~NestedGroupWriteProvider() = default;
virtual Status prepare(const ColumnVariant& variant, const TabletColumn* tablet_column,
const ColumnWriterOptions& opts, OlapBlockDataConvertor* converter,
int* column_id, VariantStatistics* statistics) = 0;
virtual Status prepare_with_built_groups(const NestedGroupsMap& nested_groups,
const TabletColumn* tablet_column,
const ColumnWriterOptions& opts,
OlapBlockDataConvertor* converter, int* column_id,
VariantStatistics* statistics) = 0;
virtual Status init_with_plan(const NestedGroupStreamingWritePlan& plan,
const TabletColumn* tablet_column,
const ColumnWriterOptions& opts, int* column_id,
VariantStatistics* statistics) = 0;
virtual Status append_chunk(const NestedGroupStreamingWritePlan& plan,
const ColumnVariant& variant) = 0;
virtual uint64_t estimate_buffer_size() const = 0;
virtual Status finish() = 0;
virtual Status write_data() = 0;
virtual Status write_ordinal_index() = 0;
virtual Status write_zone_map() = 0;
virtual Status write_inverted_index() = 0;
virtual Status write_bloom_filter_index() = 0;
};
// --------------------------------------------------------------------------
// Read provider
// --------------------------------------------------------------------------
// Extension point for NestedGroup read path.
// Concrete behavior is selected by create_nested_group_read_provider().
// The default provider is disabled and falls back to generic variant reads.
// Downstream integrations may provide a full implementation for nested
// array<object> access.
class NestedGroupReadProvider {
public:
virtual ~NestedGroupReadProvider() = default;
// Whether the NestedGroup read path is available in this build.
virtual bool should_enable_nested_group_read_path() const = 0;
// --- Reader initialization ---
// Scan segment footer for NestedGroup columns and populate |out_readers|.
// Called once per VariantColumnReader::init().
virtual Status init_readers(const ColumnReaderOptions& opts,
const std::shared_ptr<SegmentFooterPB>& footer,
const std::shared_ptr<io::FileReader>& file_reader,
ColumnMetaAccessor* accessor, int32_t root_unique_id,
uint64_t num_rows, NestedGroupReaders& out_readers) = 0;
// --- Read planning ---
// Determines if |relative_path| should be read via the NestedGroup path and if so
// fills output parameters. Returns true if a NestedGroup plan was built.
//
// Output parameters (only valid when returning true):
// out_is_whole — true for WHOLE access, false for CHILD access
// out_type — resulting data type
// out_relative_path — the relative path in the plan
// out_child_path — child path within NestedGroup (for CHILD)
// out_pruned_path — pruned prefix for object reconstruction (for WHOLE)
// out_chain — nested group reader chain from outermost to innermost
// out_path_filter — optional path filter for pruning child columns
virtual bool try_build_read_plan(
const TabletSchema* tablet_schema, const NestedGroupReaders& readers,
const TabletColumn& target_col, const StorageReadOptions* opt, int32_t col_uid,
const PathInData& relative_path,
// outputs:
bool* out_is_whole, DataTypePtr* out_type, PathInData* out_relative_path,
std::string* out_child_path, std::string* out_pruned_path,
std::vector<const NestedGroupReader*>* out_chain,
std::optional<NestedGroupPathFilter>* out_path_filter) const = 0;
// --- Iterator creation ---
// Create a ColumnIterator for a NestedGroup read plan.
// For WHOLE access: creates an iterator that reconstructs array<variant> from element columns.
// For CHILD access: creates an iterator that reads a single child column with array offsets.
virtual Status create_nested_group_iterator(
bool is_whole, const std::vector<const NestedGroupReader*>& chain,
const std::string& child_path, const std::string& pruned_path,
const std::optional<NestedGroupPathFilter>& path_filter, ColumnIteratorUPtr* out_iter,
DataTypePtr* out_type) = 0;
// --- Search support ---
// Get total number of elements in the innermost group of the chain.
virtual Status get_total_elements(const ColumnIteratorOptions& opts,
const NestedGroupReader* leaf_group,
uint64_t* total_elements) const = 0;
// Map element-level bitmap to row-level bitmap through the nested group chain.
// Create an iterator that wraps |base_iterator| with root-level NG merge logic.
// For root variant reads, top-level NestedGroup arrays must be merged back into
// the reconstructed variant. CE no-op returns base_iterator unchanged.
virtual Status create_root_merge_iterator(ColumnIteratorUPtr base_iterator,
const NestedGroupReaders& readers,
const StorageReadOptions* opt,
ColumnIteratorUPtr* out) = 0;
virtual Status map_elements_to_parent_ords(
const std::vector<const NestedGroupReader*>& group_chain,
const ColumnIteratorOptions& opts, const roaring::Roaring& element_bitmap,
roaring::Roaring* parent_bitmap) const = 0;
};
// Factory functions. Implementations are selected at link time.
// The default providers are disabled/no-op placeholders.
std::unique_ptr<NestedGroupWriteProvider> create_nested_group_write_provider();
std::unique_ptr<NestedGroupReadProvider> create_nested_group_read_provider();
} // namespace doris::segment_v2