blob: ceb790f1ff2aaeb5ce92999bb05b6292dc7d20c7 [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.
#include "runtime/variant-value.h"
#include <cstdio>
#include <cstring>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include "common/names.h"
#include "gutil/strings/substitute.h"
#include "runtime/date-parse-util.h"
#include "runtime/date-value.h"
#include "runtime/datetime-simple-date-format-parser.h"
#include "runtime/decimal-value.inline.h"
#include "runtime/timestamp-parse-util.h"
#include "runtime/timestamp-value.h"
#include "runtime/timestamp-value.inline.h"
#include "udf/udf.h"
#include "util/coding-util.h"
using impala::datetime_parse_util::SimpleDateFormatTokenizer;
using std::string_view;
namespace impala {
// --- VariantMetadata ---
Status VariantMetadata::Init(const uint8_t* data, uint32_t len) {
if (len < 1) {
return Status("Variant metadata blob is empty");
}
uint8_t header = data[0];
version_ = header & 0x0F;
if (version_ != 1) {
return Status(Substitute(
"Unsupported variant metadata version: $0", version_));
}
is_sorted_ = (header >> 4) & 0x01;
offset_size_ = ((header >> 6) & 0x03) + 1;
int pos = 1;
if (pos + offset_size_ > len) {
return Status("Variant metadata too short for dictionary size");
}
dict_size_ = 0;
for (int i = 0; i < offset_size_; ++i) {
dict_size_ |= static_cast<uint32_t>(data[pos + i]) << (8 * i);
}
pos += offset_size_;
// Offsets array: (dict_size_ + 1) entries of offset_size_ bytes each.
offsets_ = data + pos;
int offsets_len = (dict_size_ + 1) * offset_size_;
if (pos + offsets_len > len) {
return Status("Variant metadata too short for offset array");
}
pos += offsets_len;
string_data_ = data + pos;
return Status::OK();
}
uint32_t VariantMetadata::ReadOffset(uint32_t index) const {
static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, "This code assumes little-endianness");
const uint8_t* p = offsets_ + index * offset_size_;
uint32_t val = 0;
switch (offset_size_) {
case 4:
std::memcpy(&val, p, 4);
return val;
case 2:
std::memcpy(&val, p, 2);
return val;
case 1:
return *p;
case 3:
std::memcpy(&val, p, 3);
return val;
default:
return 0;
}
}
string_view VariantMetadata::GetFieldName(uint32_t index) const {
DCHECK_LT(index, dict_size_);
uint32_t start = ReadOffset(index);
uint32_t end = ReadOffset(index + 1);
return string_view(reinterpret_cast<const char*>(string_data_ + start),
end - start);
}
int VariantMetadata::FindFieldId(string_view name) const {
if (is_sorted_) {
int lo = 0, hi = dict_size_ - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
int cmp = name.compare(GetFieldName(mid));
if (cmp == 0) return mid;
if (cmp < 0) hi = mid - 1;
else lo = mid + 1;
}
} else {
for (uint32_t i = 0; i < dict_size_; ++i) {
if (name == GetFieldName(i)) return i;
}
}
return -1;
}
// --- VariantValue ---
uint32_t VariantValue::ReadUint(const uint8_t* data, uint32_t size) {
static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, "This code assumes little-endianness");
uint32_t val = 0;
switch (size) {
case 4:
std::memcpy(&val, data, 4);
return val;
case 2:
std::memcpy(&val, data, 2);
return val;
case 1:
return *data;
case 3:
std::memcpy(&val, data, 3);
return val;
default:
return 0;
}
}
VariantBasicType VariantValue::GetBasicType() const {
DCHECK(data_ != nullptr);
return static_cast<VariantBasicType>(data_[0] & 0x03);
}
VariantPhysicalType VariantValue::GetPhysicalType() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::PRIMITIVE);
return static_cast<VariantPhysicalType>((data_[0] >> 2) & 0x3F);
}
bool VariantValue::IsNull() const {
return GetBasicType() == VariantBasicType::PRIMITIVE
&& GetPhysicalType() == VariantPhysicalType::VNULL;
}
bool VariantValue::GetBoolean() const {
VariantPhysicalType pt = GetPhysicalType();
DCHECK(pt == VariantPhysicalType::BOOLEAN_TRUE
|| pt == VariantPhysicalType::BOOLEAN_FALSE);
return pt == VariantPhysicalType::BOOLEAN_TRUE;
}
int8_t VariantValue::GetInt8() const {
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::INT8);
return static_cast<int8_t>(data_[1]);
}
int16_t VariantValue::GetInt16() const {
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::INT16);
return ReadValue<int16_t>();
}
int32_t VariantValue::GetInt32() const {
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::INT32);
return ReadValue<int32_t>();
}
int64_t VariantValue::GetInt64() const {
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::INT64);
return ReadValue<int64_t>();
}
float VariantValue::GetFloat() const {
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::FLOAT);
return ReadValue<float>();
}
double VariantValue::GetDouble() const {
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::DOUBLE);
return ReadValue<double>();
}
StringValue VariantValue::GetString() const {
VariantBasicType bt = GetBasicType();
if (bt == VariantBasicType::SHORT_STRING) {
int str_len = (data_[0] >> 2) & 0x3F;
StringValue sv;
sv.Assign(reinterpret_cast<char*>(const_cast<uint8_t*>(data_ + 1)),
str_len);
return sv;
}
DCHECK_EQ(bt, VariantBasicType::PRIMITIVE);
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::STRING);
uint32_t str_len = ReadUint(data_ + 1, 4);
StringValue sv;
sv.Assign(reinterpret_cast<char*>(const_cast<uint8_t*>(data_ + 5)),
str_len);
return sv;
}
StringValue VariantValue::GetBinary() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::PRIMITIVE);
DCHECK_EQ(GetPhysicalType(), VariantPhysicalType::BINARY);
uint32_t bin_len = ReadUint(data_ + 1, 4);
StringValue sv;
sv.Assign(reinterpret_cast<char*>(const_cast<uint8_t*>(data_ + 5)),
bin_len);
return sv;
}
// --- Object accessors ---
uint32_t VariantValue::ObjectFieldIdSize() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::OBJECT);
return ((data_[0] >> 4) & 0x03) + 1;
}
uint32_t VariantValue::ObjectOffsetSize() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::OBJECT);
return ((data_[0] >> 2) & 0x03) + 1;
}
uint32_t VariantValue::ObjectNumFields() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::OBJECT);
bool is_large = (data_[0] >> 6) & 0x01;
uint32_t size_bytes = is_large ? 4 : 1;
return ReadUint(data_ + 1, size_bytes);
}
const uint8_t* VariantValue::ObjectFieldIdsStart() const {
bool is_large = (data_[0] >> 6) & 0x01;
uint32_t size_bytes = is_large ? 4 : 1;
return data_ + 1 + size_bytes;
}
const uint8_t* VariantValue::ObjectOffsetsStart() const {
uint32_t num_fields = ObjectNumFields();
uint32_t field_id_size = ObjectFieldIdSize();
return ObjectFieldIdsStart() + num_fields * field_id_size;
}
const uint8_t* VariantValue::ObjectDataStart() const {
uint32_t num_fields = ObjectNumFields();
uint32_t offset_size = ObjectOffsetSize();
return ObjectOffsetsStart() + (num_fields + 1) * offset_size;
}
uint32_t VariantValue::GetObjectSize() const {
if (GetBasicType() != VariantBasicType::OBJECT) return 0;
return ObjectNumFields();
}
bool VariantValue::GetFieldByName(string_view name,
VariantValue* result) const {
if (GetBasicType() != VariantBasicType::OBJECT) return false;
if (metadata_ == nullptr) return false;
int field_id = metadata_->FindFieldId(name);
if (field_id < 0) return false;
uint32_t num_fields = ObjectNumFields();
uint32_t field_id_size = ObjectFieldIdSize();
const uint8_t* field_ids = ObjectFieldIdsStart();
// Search for this field_id in the object's field_id array.
int field_index = -1;
for (uint32_t i = 0; i < num_fields; ++i) {
uint32_t fid = ReadUint(field_ids + i * field_id_size, field_id_size);
if (fid == field_id) {
field_index = i;
break;
}
}
if (field_index < 0) return false;
return GetFieldByIndex(field_index, result);
}
bool VariantValue::GetFieldByIndex(uint32_t index, VariantValue* result) const {
if (GetBasicType() != VariantBasicType::OBJECT) return false;
uint32_t num_fields = ObjectNumFields();
if (index >= num_fields) return false;
uint32_t offset_size = ObjectOffsetSize();
const uint8_t* offsets = ObjectOffsetsStart();
const uint8_t* data_start = ObjectDataStart();
uint32_t field_offset =
ReadUint(offsets + index * offset_size, offset_size);
uint32_t next_offset =
ReadUint(offsets + (index + 1) * offset_size, offset_size);
uint32_t field_len = next_offset - field_offset;
*result = VariantValue(data_start + field_offset, field_len, metadata_);
return true;
}
string_view VariantValue::GetFieldNameByIndex(uint32_t index) const {
DCHECK_EQ(GetBasicType(), VariantBasicType::OBJECT);
uint32_t field_id_size = ObjectFieldIdSize();
const uint8_t* field_ids = ObjectFieldIdsStart();
uint32_t fid =
ReadUint(field_ids + index * field_id_size, field_id_size);
return metadata_->GetFieldName(fid);
}
// --- Array accessors ---
uint32_t VariantValue::ArrayOffsetSize() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::ARRAY);
return ((data_[0] >> 2) & 0x03) + 1;
}
uint32_t VariantValue::ArrayNumElements() const {
DCHECK_EQ(GetBasicType(), VariantBasicType::ARRAY);
bool is_large = (data_[0] >> 4) & 0x01;
uint32_t size_bytes = is_large ? 4 : 1;
return ReadUint(data_ + 1, size_bytes);
}
const uint8_t* VariantValue::ArrayOffsetsStart() const {
bool is_large = (data_[0] >> 4) & 0x01;
uint32_t size_bytes = is_large ? 4 : 1;
return data_ + 1 + size_bytes;
}
const uint8_t* VariantValue::ArrayDataStart() const {
uint32_t num_elements = ArrayNumElements();
uint32_t offset_size = ArrayOffsetSize();
return ArrayOffsetsStart() + (num_elements + 1) * offset_size;
}
uint32_t VariantValue::GetArraySize() const {
if (GetBasicType() != VariantBasicType::ARRAY) return 0;
return ArrayNumElements();
}
bool VariantValue::GetArrayElement(uint32_t index, VariantValue* result) const {
if (GetBasicType() != VariantBasicType::ARRAY) return false;
uint32_t num_elements = ArrayNumElements();
if (index >= num_elements) return false;
uint32_t offset_size = ArrayOffsetSize();
const uint8_t* offsets = ArrayOffsetsStart();
const uint8_t* data_start = ArrayDataStart();
uint32_t elem_offset =
ReadUint(offsets + index * offset_size, offset_size);
uint32_t next_offset =
ReadUint(offsets + (index + 1) * offset_size, offset_size);
uint32_t elem_len = next_offset - elem_offset;
*result = VariantValue(data_start + elem_offset, elem_len, metadata_);
return true;
}
// --- Path navigation ---
bool VariantValue::NavigatePath(const string& path,
VariantValue* result) const {
*result = *this;
if (path == "$") return true;
const char* p = path.data();
const char* end = p + path.size();
// Path must start with '$'.
if (p >= end || *p != '$') return false;
++p;
// '$' alone is handled above; after '$' we need '.' or '['.
if (p >= end) return false;
if (*p != '.' && *p != '[') return false;
if (*p == '.') ++p;
// Must have at least one segment after the prefix.
if (p >= end) return false;
while (p < end) {
if (*p == '[') {
++p;
// Require at least one digit.
const char* digits_start = p;
int index = 0;
while (p < end && *p >= '0' && *p <= '9') {
index = index * 10 + (*p - '0');
++p;
}
if (p == digits_start) return false;
if (p >= end || *p != ']') return false;
++p; // skip ']'
if (!result->GetArrayElement(index, result)) return false;
if (p < end && *p == '.') ++p;
} else {
const char* seg_start = p;
while (p < end && *p != '.' && *p != '[') ++p;
int seg_len = p - seg_start;
if (seg_len == 0) return false;
if (!result->GetFieldByName(string_view(seg_start, seg_len), result)) {
return false;
}
if (p < end && *p == '.') ++p;
}
}
return true;
}
// --- JSON serialization ---
using JsonWriter = rapidjson::Writer<rapidjson::StringBuffer>;
static Status ValueToJson(const VariantValue& val,
const VariantMetadata& metadata, JsonWriter* writer,
impala_udf::FunctionContext* ctx = nullptr) {
switch (val.GetBasicType()) {
case VariantBasicType::SHORT_STRING: {
StringValue sv = val.GetString();
writer->String(sv.Ptr(), sv.Len());
return Status::OK();
}
case VariantBasicType::PRIMITIVE: {
switch (val.GetPhysicalType()) {
case VariantPhysicalType::VNULL:
writer->Null();
break;
case VariantPhysicalType::BOOLEAN_TRUE:
writer->Bool(true);
break;
case VariantPhysicalType::BOOLEAN_FALSE:
writer->Bool(false);
break;
case VariantPhysicalType::INT8:
writer->Int(val.GetInt8());
break;
case VariantPhysicalType::INT16:
writer->Int(val.GetInt16());
break;
case VariantPhysicalType::INT32:
writer->Int(val.GetInt32());
break;
case VariantPhysicalType::INT64:
writer->Int64(val.GetInt64());
break;
case VariantPhysicalType::FLOAT: {
char buf[24];
int n = snprintf(buf, sizeof(buf), "%g", val.GetFloat());
writer->RawValue(buf, n, rapidjson::kNumberType);
break;
}
case VariantPhysicalType::DOUBLE:
writer->Double(val.GetDouble());
break;
case VariantPhysicalType::STRING: {
StringValue sv = val.GetString();
writer->String(sv.Ptr(), sv.Len());
break;
}
case VariantPhysicalType::DATE: {
DateValue dv(static_cast<int64_t>(val.ReadValue<int32_t>()));
char buf[SimpleDateFormatTokenizer::DEFAULT_DATE_FMT_LEN];
int n = DateParser::FormatDefault(dv, buf);
if (LIKELY(n > 0)) {
DCHECK_LE(n, sizeof(buf));
writer->String(buf, n);
} else {
if (ctx) {
ctx->AddWarning("Invalid DATE value in VARIANT");
}
writer->String("<invalid-date>");
}
break;
}
case VariantPhysicalType::DECIMAL4: {
int scale = val.Data()[1];
int32_t unscaled = val.ReadValue<int32_t>(2);
string s = Decimal4Value(unscaled).ToString(9, scale);
writer->RawValue(s.data(), s.size(), rapidjson::kNumberType);
break;
}
case VariantPhysicalType::DECIMAL8: {
int scale = val.Data()[1];
int64_t unscaled = val.ReadValue<int64_t>(2);
string s = Decimal8Value(unscaled).ToString(18, scale);
writer->RawValue(s.data(), s.size(), rapidjson::kNumberType);
break;
}
case VariantPhysicalType::DECIMAL16: {
int scale = val.Data()[1];
__int128_t unscaled = val.ReadValue<__int128_t>(2);
string s = Decimal16Value(unscaled).ToString(38, scale);
writer->RawValue(s.data(), s.size(), rapidjson::kNumberType);
break;
}
case VariantPhysicalType::TIMESTAMPNTZ: {
int64_t micros = val.ReadValue<int64_t>();
TimestampValue ts = TimestampValue::UtcFromUnixTimeMicros(micros);
char buf[SimpleDateFormatTokenizer::DEFAULT_DATE_TIME_FMT_LEN];
int n = TimestampParser::FormatDefault(ts.date(), ts.time(), buf);
if (LIKELY(n > 0)) {
DCHECK_LE(n, sizeof(buf));
writer->String(buf, n);
} else {
if (ctx) {
ctx->AddWarning("Invalid TIMESTAMP value in VARIANT");
}
writer->String("<invalid-timestamp>");
}
break;
}
case VariantPhysicalType::TIMESTAMPNTZ_NANOS: {
int64_t nanos = val.ReadValue<int64_t>();
TimestampValue ts =
TimestampValue::UtcFromUnixTimeLimitedRangeNanos(nanos);
char buf[SimpleDateFormatTokenizer::DEFAULT_DATE_TIME_FMT_LEN];
int n = TimestampParser::FormatDefault(ts.date(), ts.time(), buf);
if (LIKELY(n > 0)) {
DCHECK_LE(n, sizeof(buf));
writer->String(buf, n);
} else {
if (ctx) {
ctx->AddWarning("Invalid timestamp value in VARIANT");
}
writer->String("<invalid-timestamp>");
}
break;
}
case VariantPhysicalType::BINARY: {
StringValue sv = val.GetBinary();
int64_t out_max;
if (UNLIKELY(!Base64EncodeBufLen(sv.Len(), &out_max))) {
if (ctx) {
ctx->AddWarning("Invalid BINARY value in VARIANT");
}
writer->String("<invalid-binary>");
break;
}
string encoded(out_max, '\0');
unsigned out_len;
Base64Encode(sv.Ptr(), sv.Len(), out_max, encoded.data(), &out_len);
writer->String(encoded.data(), out_len);
break;
}
case VariantPhysicalType::TIMESTAMPTZ:
case VariantPhysicalType::TIME:
case VariantPhysicalType::TIMESTAMPTZ_NANOS:
case VariantPhysicalType::UUID:
// TODO: implement proper formatting for these types.
writer->String("<unsupported-type>");
break;
}
return Status::OK();
}
case VariantBasicType::OBJECT: {
writer->StartObject();
uint32_t num_fields = val.GetObjectSize();
for (uint32_t i = 0; i < num_fields; ++i) {
string_view field_name = val.GetFieldNameByIndex(i);
writer->Key(field_name.data(), field_name.size());
VariantValue child;
if (!val.GetFieldByIndex(i, &child)) {
return Status("Failed to read object field");
}
RETURN_IF_ERROR(ValueToJson(child, metadata, writer));
}
writer->EndObject();
return Status::OK();
}
case VariantBasicType::ARRAY: {
writer->StartArray();
uint32_t num_elements = val.GetArraySize();
for (uint32_t i = 0; i < num_elements; ++i) {
VariantValue elem;
if (!val.GetArrayElement(i, &elem)) {
return Status("Failed to read array element");
}
RETURN_IF_ERROR(ValueToJson(elem, metadata, writer));
}
writer->EndArray();
return Status::OK();
}
}
return Status("Unknown variant basic type");
}
inline rapidjson::StringBuffer CreateStringBuffer(size_t expected_size) {
constexpr auto default_capacity = rapidjson::StringBuffer::kDefaultCapacity;
size_t capacity = max(expected_size, default_capacity);
return {/*allocator=*/nullptr, capacity};
}
Status VariantValue::ToJson(std::string* json_out) const {
DCHECK(metadata_ != nullptr);
auto buffer = CreateStringBuffer(Len() * 2);
JsonWriter writer(buffer);
RETURN_IF_ERROR(ValueToJson(*this, *metadata_, &writer));
json_out->assign(buffer.GetString(), buffer.GetSize());
return Status::OK();
}
Status VariantValue::ToJson(impala_udf::FunctionContext* ctx,
impala_udf::StringVal* result) const {
DCHECK(metadata_ != nullptr);
auto buffer = CreateStringBuffer(Len() * 2);
JsonWriter writer(buffer);
RETURN_IF_ERROR(ValueToJson(*this, *metadata_, &writer, ctx));
*result = impala_udf::StringVal::CopyFrom(ctx,
reinterpret_cast<const uint8_t*>(buffer.GetString()), buffer.GetSize());
return Status::OK();
}
} // namespace impala