|
| 1 | +/* |
| 2 | + * Copyright 2026-present Alibaba Inc. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +#pragma once |
| 18 | + |
| 19 | +#include <cstdint> |
| 20 | +#include <memory> |
| 21 | +#include <optional> |
| 22 | +#include <string> |
| 23 | +#include <string_view> |
| 24 | +#include <unordered_map> |
| 25 | + |
| 26 | +#include "paimon/memory/memory_pool.h" |
| 27 | +#include "paimon/predicate/literal.h" |
| 28 | +#include "paimon/result.h" |
| 29 | +#include "paimon/visibility.h" |
| 30 | + |
| 31 | +struct ArrowArray; |
| 32 | +struct ArrowSchema; |
| 33 | + |
| 34 | +namespace paimon { |
| 35 | + |
| 36 | +/// Arguments controlling how a variant value is cast to a target type in `Variant::VariantGet`. |
| 37 | +struct PAIMON_EXPORT VariantCastArgs { |
| 38 | + /// Whether an invalid cast fails the call (true) or yields SQL NULL (false). |
| 39 | + bool fail_on_error = true; |
| 40 | + /// The time zone used when rendering TIMESTAMP values. Supported forms are `UTC`/`Z`/`GMT`, |
| 41 | + /// fixed offsets such as `+08:00`, and IANA region ids such as `Asia/Shanghai`. |
| 42 | + std::string zone_id = "UTC"; |
| 43 | +}; |
| 44 | + |
| 45 | +/// A Variant represents a type that contains one of: 1) Primitive: A type and corresponding |
| 46 | +/// value (e.g. INT, STRING); 2) Array: An ordered list of Variant values; 3) Object: An |
| 47 | +/// unordered collection of string/Variant pairs (i.e. key/value pairs). An object may not |
| 48 | +/// contain duplicate keys. |
| 49 | +/// |
| 50 | +/// A Variant is encoded with 2 binaries: the value and the metadata, following the parquet |
| 51 | +/// Variant Binary Encoding specification (compatible with the Java / Spark implementation). The |
| 52 | +/// encoding allows representation of semi-structured data (e.g. JSON) in a form that can be |
| 53 | +/// efficiently queried by path. |
| 54 | +/// |
| 55 | +/// In an Arrow schema, a Variant field is represented as |
| 56 | +/// `struct<value: binary not null, metadata: binary not null>` marked with Paimon-specific field |
| 57 | +/// metadata; use `Variant::ArrowField` to construct such a field. |
| 58 | +class PAIMON_EXPORT Variant { |
| 59 | + public: |
| 60 | + ~Variant(); |
| 61 | + |
| 62 | + /// Parses a JSON string as a Variant (duplicate object keys are rejected). |
| 63 | + /// |
| 64 | + /// @param json The JSON document text. |
| 65 | + /// @param pool The memory pool used for the variant buffers. |
| 66 | + /// @return A result containing the created variant or an error. |
| 67 | + static Result<std::unique_ptr<Variant>> FromJson(const std::string& json, |
| 68 | + const std::shared_ptr<MemoryPool>& pool); |
| 69 | + |
| 70 | + /// Creates a Variant from already-encoded value and metadata binaries (copied into `pool`). |
| 71 | + /// |
| 72 | + /// @param value The variant value binary. |
| 73 | + /// @param value_length The length of the value binary. |
| 74 | + /// @param metadata The variant metadata binary. |
| 75 | + /// @param metadata_length The length of the metadata binary. |
| 76 | + /// @param pool The memory pool used for the variant buffers. |
| 77 | + /// @return A result containing the created variant or an error. |
| 78 | + static Result<std::unique_ptr<Variant>> Create(const char* value, uint64_t value_length, |
| 79 | + const char* metadata, uint64_t metadata_length, |
| 80 | + const std::shared_ptr<MemoryPool>& pool); |
| 81 | + |
| 82 | + /// The variant value binary. The view remains valid as long as this variant exists. |
| 83 | + std::string_view Value() const; |
| 84 | + |
| 85 | + /// The variant metadata binary. The view remains valid as long as this variant exists. |
| 86 | + std::string_view Metadata() const; |
| 87 | + |
| 88 | + /// The size of the variant in bytes (value size + metadata size). |
| 89 | + int64_t SizeInBytes() const; |
| 90 | + |
| 91 | + /// Stringifies the variant in JSON format. |
| 92 | + /// |
| 93 | + /// @param zone_id The time zone used when rendering TIMESTAMP values. |
| 94 | + /// @return A result containing the JSON text or an error. |
| 95 | + Result<std::string> ToJson(const std::string& zone_id = "UTC") const; |
| 96 | + |
| 97 | + /// Extracts a sub-variant value according to a path which starts with a `$`, e.g. `$.key`, |
| 98 | + /// `$['key']`, `$["key"]`, `$.array[0]`, and casts the value to the target type. |
| 99 | + /// |
| 100 | + /// @param path The extraction path. |
| 101 | + /// @param target_type The target Arrow type (C data interface, consumed by the call); only |
| 102 | + /// scalar types are supported. Use `VariantGetArrow` for nested targets. |
| 103 | + /// @param cast_args Cast behavior arguments. |
| 104 | + /// @return A result containing the extracted literal, or nullopt for SQL NULL (unmatched |
| 105 | + /// path, variant null, or an invalid cast with `fail_on_error == false`). |
| 106 | + Result<std::optional<Literal>> VariantGet(const std::string& path, |
| 107 | + struct ArrowSchema* target_type, |
| 108 | + const VariantCastArgs& cast_args) const; |
| 109 | + |
| 110 | + /// Extracts a sub-variant value according to a path which starts with a `$` and casts the |
| 111 | + /// value to the (possibly nested) target field type. |
| 112 | + /// |
| 113 | + /// In addition to scalar types, the target may be a STRUCT (cast from a variant object, |
| 114 | + /// children matched by field name; unmatched children are null), a MAP with string keys |
| 115 | + /// (cast from a variant object), a LIST (cast from a variant array), or a variant-marked |
| 116 | + /// field created by `Variant::ArrowField` (the sub-variant is deeply re-encoded). |
| 117 | + /// |
| 118 | + /// @param path The extraction path. |
| 119 | + /// @param target_field The target Arrow field (C data interface, consumed by the call). |
| 120 | + /// @param cast_args Cast behavior arguments. |
| 121 | + /// @return A result containing a length-1 Arrow array of the target type whose single slot |
| 122 | + /// holds the result; a null slot represents SQL NULL (unmatched path, variant null, |
| 123 | + /// or an invalid cast with `fail_on_error == false`). |
| 124 | + Result<std::unique_ptr<struct ArrowArray>> VariantGetArrow( |
| 125 | + const std::string& path, struct ArrowSchema* target_field, |
| 126 | + const VariantCastArgs& cast_args) const; |
| 127 | + |
| 128 | + /// Extracts a sub-variant value according to a path which starts with a `$` and renders it |
| 129 | + /// as JSON text. |
| 130 | + /// |
| 131 | + /// @param path The extraction path. |
| 132 | + /// @param zone_id The time zone used when rendering TIMESTAMP values. |
| 133 | + /// @return A result containing the JSON text, or nullopt if the path does not match. |
| 134 | + Result<std::optional<std::string>> VariantGetJson(const std::string& path, |
| 135 | + const std::string& zone_id = "UTC") const; |
| 136 | + |
| 137 | + /// Creates an Arrow field definition for the Variant type. |
| 138 | + /// |
| 139 | + /// This function constructs an Arrow Field (internally |
| 140 | + /// `struct<value: binary not null, metadata: binary not null>`) and exports it to the C data |
| 141 | + /// interface structure `::ArrowSchema`. It automatically injects Paimon-specific metadata to |
| 142 | + /// identify the field as a VARIANT. |
| 143 | + /// |
| 144 | + /// @param field_name The name of the Arrow field. |
| 145 | + /// @param nullable Whether the field is nullable. |
| 146 | + /// @param metadata A map of key-value metadata to be attached to the field. |
| 147 | + /// @return A result containing a unique pointer to the generated `::ArrowSchema` or an error. |
| 148 | + static Result<std::unique_ptr<struct ArrowSchema>> ArrowField( |
| 149 | + const std::string& field_name, bool nullable = true, |
| 150 | + std::unordered_map<std::string, std::string> metadata = {}); |
| 151 | + |
| 152 | + private: |
| 153 | + class Impl; |
| 154 | + |
| 155 | + explicit Variant(std::unique_ptr<Impl>&& impl); |
| 156 | + |
| 157 | + std::unique_ptr<Impl> impl_; |
| 158 | +}; |
| 159 | + |
| 160 | +/// Builds a variant-access projection field: a struct field that replaces a VARIANT column in |
| 161 | +/// the read schema so that, instead of the full variant, only the described paths are extracted |
| 162 | +/// at read time (reading only the required shredded sub-columns from shredded files). |
| 163 | +/// |
| 164 | +/// Example: read `$.age` as INT64 and `$.city` as STRING from variant column `v`: |
| 165 | +/// |
| 166 | +/// VariantAccessBuilder builder; |
| 167 | +/// builder.AddField(age_type, "$.age"); |
| 168 | +/// builder.AddField(city_type, "$.city"); |
| 169 | +/// auto field = builder.Build("v"); // use in ReadContextBuilder::SetReadSchema |
| 170 | +/// |
| 171 | +/// The resulting struct has one child per added field, named by its position ("0", "1", ...). |
| 172 | +class PAIMON_EXPORT VariantAccessBuilder { |
| 173 | + public: |
| 174 | + VariantAccessBuilder(); |
| 175 | + ~VariantAccessBuilder(); |
| 176 | + |
| 177 | + /// Adds an extracted field. |
| 178 | + /// |
| 179 | + /// @param target_type The Arrow type the extracted value is cast to (C data interface, |
| 180 | + /// consumed by the call). Nested targets follow `Variant::VariantGetArrow` semantics. |
| 181 | + /// @param path The extraction path, e.g. `$.a.b` or `$.array[0]`. |
| 182 | + /// @param fail_on_error Whether an invalid cast fails the read (true) or yields SQL NULL. |
| 183 | + /// @param zone_id The time zone used when rendering TIMESTAMP values. |
| 184 | + Status AddField(struct ArrowSchema* target_type, const std::string& path, |
| 185 | + bool fail_on_error = true, const std::string& zone_id = "UTC"); |
| 186 | + |
| 187 | + /// Builds the projection field named `field_name`, to be used in the read schema in place |
| 188 | + /// of the variant column with the same name. |
| 189 | + Result<std::unique_ptr<struct ArrowSchema>> Build(const std::string& field_name) const; |
| 190 | + |
| 191 | + private: |
| 192 | + class Impl; |
| 193 | + |
| 194 | + std::unique_ptr<Impl> impl_; |
| 195 | +}; |
| 196 | + |
| 197 | +} // namespace paimon |
0 commit comments