| /* | |
| * 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 "paimon/data/variant.h" | |
| #include <utility> | |
| #include "arrow/api.h" | |
| #include "arrow/c/bridge.h" | |
| #include "paimon/common/data/variant/generic_variant.h" | |
| #include "paimon/common/data/variant/variant_access_utils.h" | |
| #include "paimon/common/data/variant/variant_defs.h" | |
| #include "paimon/common/data/variant/variant_get.h" | |
| #include "paimon/common/data/variant/variant_path_segment.h" | |
| #include "paimon/common/data/variant/variant_type_utils.h" | |
| #include "paimon/common/types/data_field.h" | |
| #include "paimon/common/utils/arrow/mem_utils.h" | |
| #include "paimon/common/utils/arrow/status_utils.h" | |
| namespace paimon { | |
| class Variant::Impl { | |
| public: | |
| Impl(std::shared_ptr<GenericVariant> variant, std::shared_ptr<MemoryPool> pool) | |
| : variant_(std::move(variant)), pool_(std::move(pool)), arrow_pool_(GetArrowPool(pool_)) {} | |
| const std::shared_ptr<GenericVariant>& GetVariant() const { | |
| return variant_; | |
| } | |
| const std::shared_ptr<MemoryPool>& GetPool() const { | |
| return pool_; | |
| } | |
| const std::shared_ptr<arrow::MemoryPool>& GetArrowMemoryPool() const { | |
| return arrow_pool_; | |
| } | |
| private: | |
| std::shared_ptr<GenericVariant> variant_; | |
| std::shared_ptr<MemoryPool> pool_; | |
| std::shared_ptr<arrow::MemoryPool> arrow_pool_; | |
| }; | |
| Variant::Variant(std::unique_ptr<Impl>&& impl) : impl_(std::move(impl)) {} | |
| Variant::~Variant() = default; | |
| Result<std::unique_ptr<Variant>> Variant::FromJson(const std::string& json, | |
| const std::shared_ptr<MemoryPool>& pool) { | |
| PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<GenericVariant> variant, | |
| GenericVariant::FromJson(json, pool)); | |
| auto impl = std::make_unique<Impl>(std::move(variant), pool); | |
| return std::unique_ptr<Variant>(new Variant(std::move(impl))); | |
| } | |
| Result<std::unique_ptr<Variant>> Variant::Create(const char* value, uint64_t value_length, | |
| const char* metadata, uint64_t metadata_length, | |
| const std::shared_ptr<MemoryPool>& pool) { | |
| PAIMON_ASSIGN_OR_RAISE( | |
| std::shared_ptr<GenericVariant> variant, | |
| GenericVariant::Create(std::string_view(value, value_length), | |
| std::string_view(metadata, metadata_length), pool)); | |
| auto impl = std::make_unique<Impl>(std::move(variant), pool); | |
| return std::unique_ptr<Variant>(new Variant(std::move(impl))); | |
| } | |
| std::string_view Variant::Value() const { | |
| return impl_->GetVariant()->RawValue(); | |
| } | |
| std::string_view Variant::Metadata() const { | |
| return impl_->GetVariant()->Metadata(); | |
| } | |
| int64_t Variant::SizeInBytes() const { | |
| return impl_->GetVariant()->SizeInBytes(); | |
| } | |
| Result<std::string> Variant::ToJson(const std::string& zone_id) const { | |
| return impl_->GetVariant()->ToJson(zone_id); | |
| } | |
| Result<std::optional<Literal>> Variant::VariantGet(const std::string& path, | |
| struct ArrowSchema* target_type, | |
| const VariantCastArgs& cast_args) const { | |
| PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Field> target_field, | |
| arrow::ImportField(target_type)); | |
| return VariantGetExecutor::Get(impl_->GetVariant(), path, target_field->type(), cast_args); | |
| } | |
| Result<std::unique_ptr<struct ArrowArray>> Variant::VariantGetArrow( | |
| const std::string& path, struct ArrowSchema* target_field, | |
| const VariantCastArgs& cast_args) const { | |
| PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Field> field, | |
| arrow::ImportField(target_field)); | |
| PAIMON_ASSIGN_OR_RAISE( | |
| std::shared_ptr<arrow::Array> array, | |
| VariantGetExecutor::GetAsArrow(impl_->GetVariant(), path, field, cast_args, | |
| impl_->GetPool(), impl_->GetArrowMemoryPool())); | |
| auto result = std::make_unique<struct ArrowArray>(); | |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportArray(*array, result.get())); | |
| return result; | |
| } | |
| Result<std::optional<std::string>> Variant::VariantGetJson(const std::string& path, | |
| const std::string& zone_id) const { | |
| PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<GenericVariant> extracted, | |
| VariantGetExecutor::ExtractByPath(impl_->GetVariant(), path)); | |
| if (extracted == nullptr) { | |
| return std::optional<std::string>(std::nullopt); | |
| } | |
| PAIMON_ASSIGN_OR_RAISE(std::string json, extracted->ToJson(zone_id)); | |
| return std::optional<std::string>(std::move(json)); | |
| } | |
| Result<std::unique_ptr<struct ArrowSchema>> Variant::ArrowField( | |
| const std::string& field_name, bool nullable, | |
| std::unordered_map<std::string, std::string> metadata) { | |
| auto variant_field = VariantTypeUtils::ToArrowField(field_name, nullable, std::move(metadata)); | |
| auto field = std::make_unique<struct ArrowSchema>(); | |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportField(*variant_field, field.get())); | |
| return field; | |
| } | |
| class VariantAccessBuilder::Impl { | |
| public: | |
| arrow::FieldVector fields; | |
| }; | |
| VariantAccessBuilder::VariantAccessBuilder() : impl_(std::make_unique<Impl>()) {} | |
| VariantAccessBuilder::~VariantAccessBuilder() = default; | |
| Status VariantAccessBuilder::AddField(struct ArrowSchema* target_type, const std::string& path, | |
| bool fail_on_error, const std::string& zone_id) { | |
| PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Field> target, | |
| arrow::ImportField(target_type)); | |
| // Validate the path eagerly so mistakes fail at build time, not at read time. | |
| PAIMON_RETURN_NOT_OK(VariantPathSegment::Parse(path)); | |
| // Keep the target field's own metadata (e.g. the variant extension marker of a | |
| // `Variant::ArrowField` target, which drives the deep re-encode cast) and add the access | |
| // description to it. | |
| std::vector<std::string> keys = {DataField::DESCRIPTION}; | |
| std::vector<std::string> values = { | |
| VariantAccessUtils::BuildVariantMetadata(path, fail_on_error, zone_id)}; | |
| if (target->metadata() != nullptr) { | |
| for (int64_t i = 0; i < target->metadata()->size(); ++i) { | |
| if (target->metadata()->key(i) == DataField::DESCRIPTION) { | |
| continue; | |
| } | |
| keys.push_back(target->metadata()->key(i)); | |
| values.push_back(target->metadata()->value(i)); | |
| } | |
| } | |
| impl_->fields.push_back(arrow::field(std::to_string(impl_->fields.size()), target->type(), | |
| /*nullable=*/true, | |
| arrow::KeyValueMetadata::Make(keys, values))); | |
| return Status::OK(); | |
| } | |
| Result<std::unique_ptr<struct ArrowSchema>> VariantAccessBuilder::Build( | |
| const std::string& field_name) const { | |
| if (impl_->fields.empty()) { | |
| return Status::Invalid("a variant-access projection needs at least one field"); | |
| } | |
| auto access_field = | |
| arrow::field(field_name, arrow::struct_(impl_->fields), /*nullable=*/true, | |
| arrow::KeyValueMetadata::Make({VariantDefs::kExtensionTypeKey}, | |
| {VariantDefs::kExtensionTypeValue})); | |
| auto field = std::make_unique<struct ArrowSchema>(); | |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportField(*access_field, field.get())); | |
| return field; | |
| } | |
| } // namespace paimon |