| /* | |
| * 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 | |
| /// \file iceberg/partition_spec.h | |
| /// Partition specs for Iceberg tables. | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <span> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <vector> | |
| #include "iceberg/iceberg_export.h" | |
| #include "iceberg/partition_field.h" | |
| #include "iceberg/result.h" | |
| #include "iceberg/type_fwd.h" | |
| #include "iceberg/util/formattable.h" | |
| #include "iceberg/util/lazy.h" | |
| namespace iceberg { | |
| /// \brief A partition spec for a Table. | |
| /// | |
| /// A partition spec is a list of partition fields, along with a unique integer ID. A | |
| /// Table may have different partition specs over its lifetime due to partition spec | |
| /// evolution. | |
| class ICEBERG_EXPORT PartitionSpec : public util::Formattable { | |
| public: | |
| static constexpr int32_t kInitialSpecId = 0; | |
| /// \brief The start ID for partition field. It is only used to generate | |
| /// partition field id for v1 metadata where it is tracked. | |
| static constexpr int32_t kLegacyPartitionDataIdStart = 1000; | |
| static constexpr int32_t kInvalidPartitionFieldId = -1; | |
| /// \brief Get an unsorted partition spec singleton. | |
| static const std::shared_ptr<PartitionSpec>& Unpartitioned(); | |
| /// \brief Get the spec ID. | |
| int32_t spec_id() const; | |
| /// \brief Get a list view of the partition fields. | |
| std::span<const PartitionField> fields() const; | |
| /// \brief Return whether this spec has no non-void partition fields. | |
| bool IsUnpartitioned() const; | |
| /// \brief Get the partition type binding to the input schema. | |
| Result<std::unique_ptr<StructType>> PartitionType(const Schema& schema) const; | |
| /// \brief Get the partition type as physically written in manifest files. | |
| Result<std::unique_ptr<StructType>> RawPartitionType(const Schema& schema) const; | |
| /// \brief Get the partition path for the given partition data. | |
| Result<std::string> PartitionPath(const PartitionValues& data) const; | |
| /// \brief Returns true if this spec is equivalent to the other, with partition field | |
| /// ids ignored. That is, if both specs have the same number of fields, field order, | |
| /// field name, source columns, and transforms. | |
| bool CompatibleWith(const PartitionSpec& other) const; | |
| std::string ToString() const override; | |
| int32_t last_assigned_field_id() const { return last_assigned_field_id_; } | |
| friend bool operator==(const PartitionSpec& lhs, const PartitionSpec& rhs) { | |
| return lhs.Equals(rhs); | |
| } | |
| /// \brief Validates the partition spec against a schema. | |
| /// \param schema The schema to validate against. | |
| /// \param allow_missing_fields Whether to skip validation for partition fields whose | |
| /// source columns have been dropped from the schema. | |
| /// \return Error status if the partition spec is invalid. | |
| Status Validate(const Schema& schema, bool allow_missing_fields) const; | |
| // \brief Validates the partition field names are unique within the partition spec and | |
| // schema. | |
| static Status ValidatePartitionName(const Schema& schema, const PartitionSpec& spec); | |
| /// \brief Get the partition fields by source ID. | |
| /// \param source_id The id of the source field. | |
| /// \return The partition fields by source ID, or NotFound if the source field is not | |
| /// found. | |
| using PartitionFieldRef = std::reference_wrapper<const PartitionField>; | |
| Result<std::vector<PartitionFieldRef>> GetFieldsBySourceId(int32_t source_id) const; | |
| /// \brief Create a PartitionSpec binding to a schema. | |
| /// \param schema The schema to bind the partition spec to. | |
| /// \param spec_id The spec ID. | |
| /// \param fields The partition fields. | |
| /// \param allowMissingFields Whether to skip validation for partition fields whose | |
| /// source columns have been dropped from the schema. | |
| /// \param last_assigned_field_id The last assigned field ID assigned to ensure new | |
| /// fields get unique IDs. | |
| /// \return A Result containing the partition spec or an error. | |
| static Result<std::unique_ptr<PartitionSpec>> Make( | |
| const Schema& schema, int32_t spec_id, std::vector<PartitionField> fields, | |
| bool allow_missing_fields, | |
| std::optional<int32_t> last_assigned_field_id = std::nullopt); | |
| /// \brief Create a PartitionSpec without binding to a schema. | |
| /// \param spec_id The spec ID. | |
| /// \param fields The partition fields. | |
| /// \param last_assigned_field_id The last assigned field ID assigned to ensure new | |
| /// fields get unique IDs. | |
| /// \return A Result containing the partition spec or an error. | |
| /// \note This method does not check whether the sort fields are valid for any schema. | |
| static Result<std::unique_ptr<PartitionSpec>> Make( | |
| int32_t spec_id, std::vector<PartitionField> fields, | |
| std::optional<int32_t> last_assigned_field_id = std::nullopt); | |
| static bool HasSequentialFieldIds(const PartitionSpec& spec); | |
| private: | |
| /// \brief Create a new partition spec. | |
| /// | |
| /// \param spec_id The spec ID. | |
| /// \param fields The partition fields. | |
| /// \param last_assigned_field_id The last assigned field ID. If not provided, it will | |
| /// be calculated from the fields. | |
| PartitionSpec(int32_t spec_id, std::vector<PartitionField> fields, | |
| std::optional<int32_t> last_assigned_field_id = std::nullopt); | |
| /// \brief Compare two partition specs for equality. | |
| bool Equals(const PartitionSpec& other) const; | |
| /// \brief Validates that there are no redundant partition fields in the spec. | |
| static Status ValidateRedundantPartitions(const PartitionSpec& spec); | |
| using SourceIdToFieldsMap = std::unordered_map<int32_t, std::vector<PartitionFieldRef>>; | |
| static Result<SourceIdToFieldsMap> InitSourceIdToFieldsMap(const PartitionSpec&); | |
| const int32_t spec_id_; | |
| std::vector<PartitionField> fields_; | |
| int32_t last_assigned_field_id_; | |
| Lazy<InitSourceIdToFieldsMap> source_id_to_fields_; | |
| }; | |
| } // namespace iceberg |