| /* | |
| * 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. | |
| */ | |
| /* This file is based on source code from the Spark Project (http://spark.apache.org/), licensed | |
| * by the Apache Software Foundation (ASF) under the Apache License, Version 2.0. See the NOTICE | |
| * file distributed with this work for additional information regarding copyright ownership. */ | |
| #pragma once | |
| #include <cstdint> | |
| #include <memory> | |
| #include <string> | |
| #include <string_view> | |
| #include <unordered_map> | |
| #include <vector> | |
| #include "paimon/common/data/variant/generic_variant.h" | |
| #include "paimon/common/data/variant/variant_binary_util.h" | |
| #include "paimon/memory/memory_pool.h" | |
| #include "paimon/result.h" | |
| namespace paimon { | |
| /// Builds variant value and metadata binaries, either by parsing JSON values or by appending | |
| /// values directly. | |
| class VariantBuilder { | |
| public: | |
| /// Temporarily stores the information of a field. All fields of a JSON object are collected, | |
| /// sorted by their keys, and then the variant object is built in sorted order. | |
| struct FieldEntry { | |
| std::string key; | |
| int32_t id; | |
| int32_t offset; | |
| FieldEntry(std::string key, int32_t id, int32_t offset) | |
| : key(std::move(key)), id(id), offset(offset) {} | |
| }; | |
| explicit VariantBuilder(bool allow_duplicate_keys) | |
| : allow_duplicate_keys_(allow_duplicate_keys) {} | |
| /// Parses a JSON string as a variant value. | |
| /// | |
| /// When `allow_duplicate_keys` is true, the last occurrence of a duplicate object key wins; | |
| /// otherwise duplicate keys make the parse fail. | |
| static Result<std::shared_ptr<GenericVariant>> ParseJson( | |
| std::string_view json, bool allow_duplicate_keys, const std::shared_ptr<MemoryPool>& pool); | |
| /// Builds the variant metadata from the collected dictionary keys and returns the variant | |
| /// result. | |
| Result<std::shared_ptr<GenericVariant>> Build(const std::shared_ptr<MemoryPool>& pool); | |
| /// The variant value written so far, without metadata. Used in shredding to produce a final | |
| /// value where all shredded values refer to a common metadata. | |
| std::string_view ValueWithoutMetadata() const { | |
| return {reinterpret_cast<const char*>(write_buffer_.data()), | |
| static_cast<size_t>(write_pos_)}; | |
| } | |
| Status AppendString(std::string_view str); | |
| Status AppendNull(); | |
| Status AppendBoolean(bool b); | |
| /// Appends a long value. The actual used integer type depends on the value range. | |
| Status AppendLong(int64_t l); | |
| Status AppendDouble(double d); | |
| /// Appends a decimal value. Its precision and scale must fit into `kMaxDecimal16Precision`. | |
| Status AppendDecimal(const VariantDecimal& d); | |
| Status AppendDate(int32_t days_since_epoch); | |
| Status AppendTimestamp(int64_t micros_since_epoch); | |
| Status AppendTimestampNtz(int64_t micros_since_epoch); | |
| Status AppendFloat(float f); | |
| Status AppendBinary(std::string_view binary); | |
| /// Appends a UUID value (16 bytes, big-endian). | |
| Status AppendUuid(std::string_view uuid_bytes); | |
| /// Adds a key to the variant dictionary and returns its id. If the key already exists, the | |
| /// dictionary is not modified. | |
| int32_t AddKey(std::string_view key); | |
| /// The current write position of the variant builder. It is used together with | |
| /// `FinishWritingObject` or `FinishWritingArray`. | |
| int32_t GetWritePos() const { | |
| return write_pos_; | |
| } | |
| /// Finishes writing a variant object after all of its fields have already been written. The | |
| /// process is as follows: | |
| /// 1. The caller calls `GetWritePos` before writing any fields to obtain the `start` | |
| /// parameter. | |
| /// 2. The caller appends all the object fields to the builder. In the meantime, it should | |
| /// maintain the `fields` parameter. Before appending each field, it should append an entry | |
| /// to `fields` to record the offset of the field, computed as `GetWritePos() - start`. | |
| /// 3. The caller calls `FinishWritingObject` to finish writing a variant object. | |
| /// | |
| /// This function sorts the fields by key. If there are duplicate field keys: | |
| /// - when `allow_duplicate_keys` is true, the field with the greatest offset value (the last | |
| /// appended one) is kept; | |
| /// - otherwise, the call fails. | |
| Status FinishWritingObject(int32_t start, std::vector<FieldEntry>* fields); | |
| /// Finishes writing a variant array after all of its elements have already been written. The | |
| /// process is similar to that of `FinishWritingObject`. | |
| Status FinishWritingArray(int32_t start, const std::vector<int32_t>& offsets); | |
| /// Appends a variant value. The keys of the input variant are inserted into the current | |
| /// variant dictionary and the value is rebuilt with new field ids. For scalar values, the | |
| /// binary slice is copied directly. | |
| Status AppendVariant(const GenericVariant& v); | |
| /// Appends the variant value without rewriting or creating any metadata. This is used when | |
| /// building an object during shredding, where there is a fixed pre-existing metadata that all | |
| /// shredded values refer to. | |
| Status ShallowAppendVariant(std::string_view value, int32_t pos); | |
| private: | |
| Status CheckCapacity(int32_t additional); | |
| Status AppendVariantImpl(std::string_view value, std::string_view metadata, int32_t pos); | |
| static int32_t GetIntegerSize(int32_t value); | |
| // The write buffer in building the variant value. Its first `write_pos_` bytes have been | |
| // written. | |
| std::vector<uint8_t> write_buffer_ = std::vector<uint8_t>(128); | |
| int32_t write_pos_ = 0; | |
| // Maps keys to a monotonically increasing id. | |
| std::unordered_map<std::string, int32_t> dictionary_; | |
| // Stores all keys in `dictionary_` in the order of id. | |
| std::vector<std::string> dictionary_keys_; | |
| const bool allow_duplicate_keys_; | |
| }; | |
| } // namespace paimon |