blob: 4c959592fcae1d6e35770c9f10a4a7a3865b6aed [file]
/*
* 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