blob: b9445222ee2f38ce0ea055abba0f1ead3edb1e0a [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.
#pragma once
#include <ostream>
#include <string>
#include <string_view>
#include "common/status.h"
#include "runtime/string-value.h"
namespace impala_udf {
class FunctionContext;
struct StringVal;
}
namespace impala {
// Physical type tags for the variant binary encoding.
// Corresponds to basic_type=0 (primitive) with type_info encoding the physical type.
enum class VariantPhysicalType : uint8_t {
VNULL = 0,
BOOLEAN_TRUE = 1,
BOOLEAN_FALSE = 2,
INT8 = 3,
INT16 = 4,
INT32 = 5,
INT64 = 6,
DOUBLE = 7,
DECIMAL4 = 8,
DECIMAL8 = 9,
DECIMAL16 = 10,
DATE = 11,
TIMESTAMPTZ = 12,
TIMESTAMPNTZ = 13,
FLOAT = 14,
BINARY = 15,
STRING = 16,
TIME = 17,
TIMESTAMPTZ_NANOS = 18,
TIMESTAMPNTZ_NANOS = 19,
UUID = 20
};
inline std::ostream& operator<<(std::ostream& os, VariantPhysicalType pt) {
switch (pt) {
case VariantPhysicalType::VNULL: return os << "VNULL";
case VariantPhysicalType::BOOLEAN_TRUE: return os << "BOOLEAN_TRUE";
case VariantPhysicalType::BOOLEAN_FALSE: return os << "BOOLEAN_FALSE";
case VariantPhysicalType::INT8: return os << "INT8";
case VariantPhysicalType::INT16: return os << "INT16";
case VariantPhysicalType::INT32: return os << "INT32";
case VariantPhysicalType::INT64: return os << "INT64";
case VariantPhysicalType::DOUBLE: return os << "DOUBLE";
case VariantPhysicalType::DECIMAL4: return os << "DECIMAL4";
case VariantPhysicalType::DECIMAL8: return os << "DECIMAL8";
case VariantPhysicalType::DECIMAL16: return os << "DECIMAL16";
case VariantPhysicalType::DATE: return os << "DATE";
case VariantPhysicalType::TIMESTAMPTZ: return os << "TIMESTAMPTZ";
case VariantPhysicalType::TIMESTAMPNTZ: return os << "TIMESTAMPNTZ";
case VariantPhysicalType::FLOAT: return os << "FLOAT";
case VariantPhysicalType::BINARY: return os << "BINARY";
case VariantPhysicalType::STRING: return os << "STRING";
case VariantPhysicalType::TIME: return os << "TIME";
case VariantPhysicalType::TIMESTAMPTZ_NANOS: return os << "TIMESTAMPTZ_NANOS";
case VariantPhysicalType::TIMESTAMPNTZ_NANOS: return os << "TIMESTAMPNTZ_NANOS";
case VariantPhysicalType::UUID: return os << "UUID";
}
return os << "UNKNOWN(" << static_cast<int>(pt) << ")";
}
// Basic type tags encoded in bits 0-1 of the value header byte.
enum class VariantBasicType : uint8_t {
PRIMITIVE = 0,
SHORT_STRING = 1,
OBJECT = 2,
ARRAY = 3
};
inline std::ostream& operator<<(std::ostream& os, VariantBasicType bt) {
switch (bt) {
case VariantBasicType::PRIMITIVE: return os << "PRIMITIVE";
case VariantBasicType::SHORT_STRING: return os << "SHORT_STRING";
case VariantBasicType::OBJECT: return os << "OBJECT";
case VariantBasicType::ARRAY: return os << "ARRAY";
}
return os << "UNKNOWN(" << static_cast<int>(bt) << ")";
}
// Parses and provides access to the variant metadata blob (field name dictionary).
// The metadata format:
// - Header byte: version (bits 0-3), sorted flag (bit 4),
// offset_size_minus_one (bits 6-7)
// - Dictionary size: variable-width integer (1-4 bytes based on offset size)
// - Offsets: (dict_size + 1) entries of offset_size bytes each
// - String data: concatenated field name strings
class VariantMetadata {
public:
VariantMetadata() = default;
// Initialize from a metadata blob. Returns error if the blob is malformed.
Status Init(const uint8_t* data, uint32_t len);
// Returns the number of field names in the dictionary.
uint32_t DictionarySize() const { return dict_size_; }
// Returns the field name at the given dictionary index.
// index must be in [0, DictionarySize()).
std::string_view GetFieldName(uint32_t index) const;
// Looks up a field name in the dictionary. Returns the index if found, -1 otherwise.
// If the dictionary is sorted, uses binary search.
int FindFieldId(std::string_view name) const;
bool IsValid() const { return offsets_ != nullptr; }
private:
uint32_t ReadOffset(uint32_t index) const;
const uint8_t* offsets_ = nullptr;
const uint8_t* string_data_ = nullptr;
uint32_t dict_size_ = 0;
uint8_t version_ = 0;
uint8_t offset_size_ = 0; // 1, 2, 3, or 4 bytes per offset
bool is_sorted_ = false;
};
// Provides access to a variant value blob. Requires a VariantMetadata for field name
// lookups when navigating objects.
//
// Value format header byte:
// bits 0-1: basic_type (PRIMITIVE=0, SHORT_STRING=1, OBJECT=2, ARRAY=3)
// bits 2-7: type_info
// - For PRIMITIVE: physical type id
// - For SHORT_STRING: string length (0-63 bytes)
// - For OBJECT: field_offset_size_minus_one (bits 2-3),
// field_id_size_minus_one (bits 4-5), is_large (bit 6)
// - For ARRAY: offset_size_minus_one (bits 2-3), is_large (bit 4)
class VariantValue {
public:
VariantValue() = default;
VariantValue(const uint8_t* data, int32_t len, const VariantMetadata* metadata)
: data_(data), len_(len), metadata_(metadata) {}
// Returns the basic type of this value.
VariantBasicType GetBasicType() const;
// Returns the physical type (only valid for PRIMITIVE basic type).
VariantPhysicalType GetPhysicalType() const;
// Returns true if this value is null.
bool IsNull() const;
// Scalar accessors. Caller must ensure the physical type matches.
bool GetBoolean() const;
int8_t GetInt8() const;
int16_t GetInt16() const;
int32_t GetInt32() const;
int64_t GetInt64() const;
float GetFloat() const;
double GetDouble() const;
StringValue GetString() const;
StringValue GetBinary() const;
// Object access.
uint32_t GetObjectSize() const;
// Gets the field value by field name. Returns false if field not found.
bool GetFieldByName(std::string_view name, VariantValue* result) const;
// Gets the field value by position index.
bool GetFieldByIndex(uint32_t index, VariantValue* result) const;
// Gets the field name at position index in this object.
std::string_view GetFieldNameByIndex(uint32_t index) const;
// Array access.
uint32_t GetArraySize() const;
bool GetArrayElement(uint32_t index, VariantValue* result) const;
// Navigate a dotted path like "field.nested[0].value".
// Returns false if the path cannot be resolved.
bool NavigatePath(const std::string& path, VariantValue* result) const;
// Serialize this variant value to JSON string.
Status ToJson(std::string* json_out) const;
Status ToJson(impala_udf::FunctionContext* ctx, impala_udf::StringVal* result) const;
bool IsValid() const { return data_ != nullptr; }
const uint8_t* Data() const { return data_; }
uint32_t Len() const { return len_; }
// Reads a primitive value of type T from the payload (data_ + offset).
// Default offset is 1 (immediately after the header byte).
template <typename T>
T ReadValue(uint32_t offset = 1) const {
DCHECK_GE(len_, offset + sizeof(T));
T val;
memcpy(&val, data_ + offset, sizeof(T));
return val;
}
private:
// Helper to read a variable-width unsigned integer.
static uint32_t ReadUint(const uint8_t* data, uint32_t size);
// For OBJECT: parse the header to get field count and internal layout.
uint32_t ObjectFieldIdSize() const;
uint32_t ObjectOffsetSize() const;
uint32_t ObjectNumFields() const;
const uint8_t* ObjectFieldIdsStart() const;
const uint8_t* ObjectOffsetsStart() const;
const uint8_t* ObjectDataStart() const;
// For ARRAY: parse the header to get element count and offsets.
uint32_t ArrayOffsetSize() const;
uint32_t ArrayNumElements() const;
const uint8_t* ArrayOffsetsStart() const;
const uint8_t* ArrayDataStart() const;
const uint8_t* data_ = nullptr;
uint32_t len_ = 0;
const VariantMetadata* metadata_ = nullptr;
};
} // namespace impala