| /* | |
| * 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. | |
| */ | |
| #include "iceberg/sort_order.h" | |
| #include <format> | |
| #include <memory> | |
| #include <optional> | |
| #include <ranges> | |
| #include "iceberg/result.h" | |
| #include "iceberg/schema.h" | |
| #include "iceberg/sort_field.h" | |
| #include "iceberg/transform.h" | |
| #include "iceberg/util/formatter.h" // IWYU pragma: keep | |
| #include "iceberg/util/macros.h" | |
| namespace iceberg { | |
| SortOrder::SortOrder(int32_t order_id, std::vector<SortField> fields) | |
| : order_id_(order_id), fields_(std::move(fields)) {} | |
| const std::shared_ptr<SortOrder>& SortOrder::Unsorted() { | |
| static const std::shared_ptr<SortOrder> unsorted = std::shared_ptr<SortOrder>( | |
| new SortOrder(kUnsortedOrderId, std::vector<SortField>{})); | |
| return unsorted; | |
| } | |
| int32_t SortOrder::order_id() const { return order_id_; } | |
| std::span<const SortField> SortOrder::fields() const { return fields_; } | |
| bool SortOrder::Satisfies(const SortOrder& other) const { | |
| // any ordering satisfies an unsorted ordering | |
| if (other.is_unsorted()) { | |
| return true; | |
| } | |
| // this ordering cannot satisfy an ordering with more sort fields | |
| if (fields_.size() < other.fields().size()) { | |
| return false; | |
| } | |
| // this ordering has either more or the same number of sort fields | |
| for (const auto& [field, other_field] : std::views::zip(fields_, other.fields_)) { | |
| if (!field.Satisfies(other_field)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool SortOrder::SameOrder(const SortOrder& other) const { | |
| return fields_ == other.fields_; | |
| } | |
| std::string SortOrder::ToString() const { | |
| std::string repr = "["; | |
| for (const auto& field : fields_) { | |
| std::format_to(std::back_inserter(repr), "\n {}", field); | |
| } | |
| if (!fields_.empty()) { | |
| repr.push_back('\n'); | |
| } | |
| repr += "]"; | |
| return repr; | |
| } | |
| bool SortOrder::Equals(const SortOrder& other) const { | |
| return order_id_ == other.order_id_ && fields_ == other.fields_; | |
| } | |
| Status SortOrder::Validate(const Schema& schema) const { | |
| for (const auto& field : fields_) { | |
| ICEBERG_ASSIGN_OR_RAISE(auto schema_field, schema.FindFieldById(field.source_id())); | |
| if (!schema_field.has_value()) { | |
| return InvalidArgument("Cannot find source column for sort field: {}", field); | |
| } | |
| const auto& source_type = schema_field.value().get().type(); | |
| ICEBERG_RETURN_UNEXPECTED(field.transform()->Validate(source_type)); | |
| } | |
| return {}; | |
| } | |
| Result<std::unique_ptr<SortOrder>> SortOrder::Make(const Schema& schema, int32_t sort_id, | |
| std::vector<SortField> fields) { | |
| if (!fields.empty() && sort_id == kUnsortedOrderId) [[unlikely]] { | |
| return InvalidArgument("{} is reserved for unsorted sort order", kUnsortedOrderId); | |
| } | |
| if (fields.empty() && sort_id != kUnsortedOrderId) [[unlikely]] { | |
| return InvalidArgument("Sort order must have at least one sort field."); | |
| } | |
| auto sort_order = std::unique_ptr<SortOrder>(new SortOrder(sort_id, std::move(fields))); | |
| ICEBERG_RETURN_UNEXPECTED(sort_order->Validate(schema)); | |
| return sort_order; | |
| } | |
| Result<std::unique_ptr<SortOrder>> SortOrder::Make(int32_t sort_id, | |
| std::vector<SortField> fields) { | |
| if (!fields.empty() && sort_id == kUnsortedOrderId) [[unlikely]] { | |
| return InvalidArgument("{} is reserved for unsorted sort order", kUnsortedOrderId); | |
| } | |
| if (fields.empty() && sort_id != kUnsortedOrderId) [[unlikely]] { | |
| return InvalidArgument("Sort order must have at least one sort field"); | |
| } | |
| return std::unique_ptr<SortOrder>(new SortOrder(sort_id, std::move(fields))); | |
| } | |
| std::unordered_set<std::string_view> SortOrder::OrderPreservingSortedColumns( | |
| const Schema& schema, const SortOrder& order) { | |
| return order.fields() | std::views::filter([&schema](const SortField& field) { | |
| return field.transform()->PreservesOrder(); | |
| }) | | |
| std::views::transform([&schema](const SortField& field) { | |
| return schema.FindColumnNameById(field.source_id()) | |
| .value_or(std::nullopt) | |
| .value_or(""); | |
| }) | | |
| std::views::filter([](std::string_view name) { return !name.empty(); }) | | |
| std::ranges::to<std::unordered_set<std::string_view>>(); | |
| } | |
| } // namespace iceberg |