| /* |
| * 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(); |
| |
| if (!field.transform()->CanTransform(*source_type)) { |
| return InvalidArgument("Invalid source type {} for transform {}", |
| source_type->ToString(), field.transform()->ToString()); |
| } |
| } |
| 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 |