HappenLee commented on code in PR #65805:
URL: https://github.com/apache/doris/pull/65805#discussion_r3747683558
##########
be/src/storage/segment/column_reader.cpp:
##########
@@ -90,25 +92,260 @@ inline bool read_as_string(PrimitiveType type) {
type == PrimitiveType::TYPE_BITMAP || type ==
PrimitiveType::TYPE_FIXED_LENGTH_OBJECT;
}
-bool is_current_level_meta_access_path(const TColumnAccessPath& path) {
- if (path.data_access_path.path.size() != 1) {
- return false;
- }
- const auto& component = path.data_access_path.path[0];
+bool is_meta_access_path_component(const std::string& component) {
return StringCaseEqual()(component, ColumnIterator::ACCESS_OFFSET) ||
StringCaseEqual()(component, ColumnIterator::ACCESS_NULL);
}
-bool is_current_level_data_access_path(const TColumnAccessPath& path,
- const std::string& column_name) {
- return path.data_access_path.path.size() == 1 &&
- StringCaseEqual()(path.data_access_path.path[0], column_name);
+bool uses_legacy_access_path_encoding(const TColumnAccessPath& path) {
+ return !path.__isset.version ||
+ path.version ==
g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_LEGACY;
}
-void remove_current_level_meta_access_paths(TColumnAccessPaths& paths) {
- auto removed = std::ranges::remove_if(paths,
is_current_level_meta_access_path);
- paths.erase(removed.begin(), removed.end());
-}
+namespace {
+
+// Nested access paths are processed one container level at a time:
+//
+// 1. Each Map/Array/Struct iterator's set_access_paths() calls
_prepare_nested_access_paths(). For
+// the all-path and predicate-path channels independently,
_split_access_paths() validates the
+// wire encoding and type-selected payload, removes the current iterator
name, and separates
+// requests consumed by the current iterator from paths that still address
a data descendant. A
+// current DATA request requires all data children. Struct owns
current-level NULL metadata;
+// Map and Array own NULL and OFFSET metadata. A supported metadata-only
request can stop before
+// descendant routing and mark every data child SKIP.
+// 2. This router interprets only the first remaining component and routes the
path according to
+// the container topology:
+// - Struct components already name fields. Select the paths for each field
without rewriting.
+// - Array `*` names its only item. Retarget `*` to the item iterator name.
+// - Map `KEYS` and `VALUES` directly name its logical children. Map `*`
creates a complete DATA
+// path for `KEYS` and routes any trailing components through `VALUES`.
+// 3. The container forwards the routed all-path and predicate-path channels
to each selected
+// child's set_access_paths(), then finalizes that child's
PREDICATE/LAZY_OUTPUT/SKIP
+// requirement. The child repeats the same flow, which handles arbitrary
Map/Array/Struct
+// nesting.
+//
+// At this point versions have been validated, legacy paths have DATA type,
and every payload
+// selected by type is non-empty. Routing never interprets or rewrites an
unselected compatibility
+// payload.
+class DescendantAccessPathRouter final {
+public:
+ DescendantAccessPathRouter() = delete;
+
+ // Preserve the two set_access_paths() input channels. all_paths
originates from the
+ // all-access-path superset, while predicate_paths separately records
predicate-phase paths.
+ // Routing may omit all_paths when the parent already requires complete
child data.
+ struct ChildAccessPaths {
+ TColumnAccessPaths all_paths;
+ TColumnAccessPaths predicate_paths;
+
+ bool empty() const { return all_paths.empty() &&
predicate_paths.empty(); }
+ };
+
+ struct MapChildAccessPaths {
+ ChildAccessPaths key;
+ ChildAccessPaths value;
+ };
+
+ // Map children use the logical KEYS/VALUES selectors as their access-path
names, independent
+ // of physical child column names. Expand a wildcard to complete keys and
route its trailing
+ // qualifiers to values.
+ static Result<MapChildAccessPaths> route_map_paths_to_children(
+ TColumnAccessPaths all_paths, TColumnAccessPaths predicate_paths) {
+ MapChildAccessPaths child_paths;
+ auto status = distribute_map_paths(std::move(all_paths),
child_paths.key.all_paths,
+ child_paths.value.all_paths);
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ status = distribute_map_paths(std::move(predicate_paths),
child_paths.key.predicate_paths,
+ child_paths.value.predicate_paths);
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ return child_paths;
+ }
+
+ // Array has one data child. Retarget its logical wildcard selector to the
item iterator name.
+ static Result<ChildAccessPaths>
route_array_paths_to_item(TColumnAccessPaths all_paths,
+
TColumnAccessPaths predicate_paths,
+ const
std::string& item_name) {
+ ChildAccessPaths child_paths {.all_paths = std::move(all_paths),
+ .predicate_paths =
std::move(predicate_paths)};
+ auto retarget_wildcard_paths_to_item = [&](TColumnAccessPaths& paths)
-> Status {
+ for (auto& path : paths) {
+ const bool is_wildcard =
DORIS_TRY(selected_payload_head_matches(
+ path, ColumnIterator::ACCESS_ALL,
PathHeadMatchMode::EXACT));
+ if (is_wildcard) {
+ RETURN_IF_ERROR(replace_selected_payload_head(path,
item_name));
+ }
+ }
+ return Status::OK();
+ };
+
+ auto status = retarget_wildcard_paths_to_item(child_paths.all_paths);
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ status = retarget_wildcard_paths_to_item(child_paths.predicate_paths);
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ return child_paths;
+ }
+
+ // Struct selectors already use child field names. Select the paths for
one child without
+ // rewriting them, so that the child can validate and strip its own name.
+ static Result<ChildAccessPaths> select_struct_paths_for_child(
+ const TColumnAccessPaths& all_paths, const TColumnAccessPaths&
predicate_paths,
+ const std::string& child_name, bool include_all_paths) {
+ ChildAccessPaths child_paths;
+ auto select_matching_paths = [&](const TColumnAccessPaths&
source_paths,
+ TColumnAccessPaths& child_paths) ->
Status {
+ for (const auto& path : source_paths) {
+ const bool matches_child =
DORIS_TRY(selected_payload_head_matches(
+ path, child_name,
PathHeadMatchMode::CASE_INSENSITIVE));
+ if (matches_child) {
+ child_paths.emplace_back(path);
+ }
+ }
+ return Status::OK();
+ };
+
+ if (include_all_paths) {
+ auto status = select_matching_paths(all_paths,
child_paths.all_paths);
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ }
+ auto status = select_matching_paths(predicate_paths,
child_paths.predicate_paths);
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ return child_paths;
+ }
+
+private:
+ enum class PathHeadMatchMode { EXACT, CASE_INSENSITIVE };
+ enum class MapSelector { WILDCARD, KEYS, VALUES };
+
+ // TColumnAccessPath may carry both payload fields after compatibility
forwarding. Selected
+ // means data_access_path for DATA and meta_access_path for META. Visit
only that payload and
+ // leave the other payload untouched.
+ template <typename AccessPath, typename Visitor>
+ static Status visit_selected_payload(AccessPath& access_path, Visitor&&
visitor) {
+ switch (access_path.type) {
+ case TAccessPathType::DATA:
+ if (!access_path.__isset.data_access_path) {
+ return Status::InternalError(
+ "Invalid DATA access path: data_access_path payload is
not set");
+ }
+ return
std::forward<Visitor>(visitor)(access_path.data_access_path);
+ case TAccessPathType::META:
+ if (!access_path.__isset.meta_access_path) {
+ return Status::InternalError(
+ "Invalid META access path: meta_access_path payload is
not set");
+ }
+ return
std::forward<Visitor>(visitor)(access_path.meta_access_path);
+ default:
+ return Status::InternalError("Invalid access path type: {}",
+ static_cast<int>(access_path.type));
+ }
+ }
+
+ static Result<bool> selected_payload_head_matches(const TColumnAccessPath&
access_path,
+ const std::string&
expected_head,
+ PathHeadMatchMode
match_mode) {
+ bool matches = false;
+ auto status = visit_selected_payload(access_path, [&](const auto&
payload) {
+ DORIS_CHECK(!payload.path.empty());
+ matches = match_mode == PathHeadMatchMode::EXACT
+ ? payload.path.front() == expected_head
+ : StringCaseEqual()(payload.path.front(),
expected_head);
+ return Status::OK();
+ });
+ if (!status.ok()) {
+ return ResultError(std::move(status));
+ }
+ return matches;
+ }
+
+ static Status replace_selected_payload_head(TColumnAccessPath& access_path,
+ const std::string& child_name)
{
+ return visit_selected_payload(access_path, [&](auto& payload) {
+ DORIS_CHECK(!payload.path.empty());
+ payload.path.front() = child_name;
+ return Status::OK();
+ });
+ }
+
+ // Map `*` applies trailing qualifiers only to values, while locating
entries still requires
Review Comment:
select length(m['xxx'])from t; `map<string, string>: ["m", "*", "OFFSET"]`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]