Skip to content

Commit 6559ca8

Browse files
committed
Fix various issues related to Decimal and Arrow
* Changelog entry Type: fix Component: proxy Some fixes in decimal: * Support parsing of nested `decimal128`/`decimal256` in Arrow. * Fix bug in `decimal256` unversioned value representation -- before this change, Arrow parser for `decimal256(n, p)` was always emitting strings of length 256 bits, event for n \<= 38, which is incorrect in the representation of YT `decimal(n, p)` type. Now it produces strings of variadic length (32, 64, 128 or 256 bits) depending on n, similar to `decimal128(n, p)`. --- Pull Request resolved: <ytsaurus/ytsaurus#942> commit_hash:32e66c7eb4d996caf0893f97d269fb1930bc5f7a
1 parent bee4516 commit 6559ca8

File tree

3 files changed

+79
-21
lines changed

3 files changed

+79
-21
lines changed

yt/yt/library/decimal/decimal.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,10 +891,28 @@ TStringBuf TDecimal::WriteBinary256(int precision, TValue256 value, char* buffer
891891
CheckDecimalIntBits<TValue256>(precision);
892892
YT_VERIFY(bufferLength >= resultLength);
893893

894-
DecimalIntegerToBinaryUnchecked(std::move(value), buffer);
894+
DecimalIntegerToBinaryUnchecked(value, buffer);
895895
return TStringBuf{buffer, sizeof(TValue256)};
896896
}
897897

898+
TStringBuf TDecimal::WriteBinary256Variadic(int precision, TValue256 value, char* buffer, size_t bufferLength)
899+
{
900+
const size_t resultLength = GetValueBinarySize(precision);
901+
switch (resultLength) {
902+
case 4:
903+
return WriteBinary32(precision, *reinterpret_cast<i32*>(value.Parts.data()), buffer, bufferLength);
904+
case 8:
905+
return WriteBinary64(precision, *reinterpret_cast<i64*>(value.Parts.data()), buffer, bufferLength);
906+
case 16:
907+
return WriteBinary128(precision, *reinterpret_cast<TValue128*>(value.Parts.data()), buffer, bufferLength);
908+
case 32:
909+
return WriteBinary256(precision, value, buffer, bufferLength);
910+
default:
911+
THROW_ERROR_EXCEPTION("Invalid precision %v", precision);
912+
}
913+
}
914+
915+
898916
template <typename T>
899917
Y_FORCE_INLINE void CheckBufferLength(int precision, size_t bufferLength)
900918
{

yt/yt/library/decimal/decimal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class TDecimal
2424
};
2525
static_assert(sizeof(TValue128) == 2 * sizeof(ui64));
2626

27+
//! Lower-endian representation of 256-bit decimal value.
2728
struct TValue256
2829
{
2930
std::array<ui32, 8> Parts;
@@ -64,6 +65,8 @@ class TDecimal
6465

6566
// Writes either 32-bit, 64-bit or 128-bit binary value depending on precision, provided a TValue128.
6667
static TStringBuf WriteBinary128Variadic(int precision, TValue128 value, char* buffer, size_t bufferLength);
68+
// Writes either 32-bit, 64-bit, 128-bit or 256-bit binary value depending on precision, provided a TValue256.
69+
static TStringBuf WriteBinary256Variadic(int precision, TValue256 value, char* buffer, size_t bufferLength);
6770

6871
static i32 ParseBinary32(int precision, TStringBuf buffer);
6972
static i64 ParseBinary64(int precision, TStringBuf buffer);

yt/yt/library/formats/arrow_parser.cpp

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ void ThrowOnError(const arrow::Status& status)
4040
}
4141
}
4242

43+
template <class TUnderlyingValueType>
44+
TStringBuf SerializeDecimalBinary(const TStringBuf& value, int precision, char* buffer, size_t bufferLength)
45+
{
46+
// NB: Arrow wire representation of Decimal128 is little-endian and (obviously) 128 bit,
47+
// while YT in-memory representation of Decimal is big-endian, variadic-length of either 32 bit, 64 bit or 128 bit,
48+
// and MSB-flipped to ensure lexical sorting order.
49+
// Representation of Decimal256 is similar, but the upper limit for a length is 256 bit.
50+
TUnderlyingValueType decimalValue;
51+
YT_VERIFY(value.size() == sizeof(decimalValue));
52+
std::memcpy(&decimalValue, value.data(), value.size());
53+
54+
TStringBuf decimalBinary;
55+
if constexpr (std::is_same_v<TUnderlyingValueType, TDecimal::TValue128>) {
56+
decimalBinary = TDecimal::WriteBinary128Variadic(precision, decimalValue, buffer, bufferLength);
57+
} else if constexpr (std::is_same_v<TUnderlyingValueType, TDecimal::TValue256>) {
58+
decimalBinary = TDecimal::WriteBinary256Variadic(precision, decimalValue, buffer, bufferLength);
59+
} else {
60+
static_assert(std::is_same_v<TUnderlyingValueType, TDecimal::TValue256>, "Unexpected decimal type");
61+
}
62+
return decimalBinary;
63+
}
64+
4365
////////////////////////////////////////////////////////////////////////////////
4466

4567
class TArraySimpleVisitor
@@ -291,28 +313,12 @@ class TArraySimpleVisitor
291313
}
292314

293315
template <class TUnderlyingValueType>
294-
TUnversionedValue MakeDecimalBinaryValue(const TStringBuf& value, i64 columnId, int precision)
316+
TUnversionedValue MakeDecimalBinaryValue(const TStringBuf& arrowValue, i64 columnId, int precision)
295317
{
296-
// NB: Arrow wire representation of Decimal128 is little-endian and (obviously) 128 bit,
297-
// while YT in-memory representation of Decimal is big-endian, variadic-length of either 32 bit, 64 bit or 128 bit,
298-
// and MSB-flipped to ensure lexical sorting order.
299-
// Representation of Decimal256 is similar, but only 256 bits.
300-
TUnderlyingValueType decimalValue;
301-
YT_VERIFY(value.size() == sizeof(decimalValue));
302-
std::memcpy(&decimalValue, value.data(), value.size());
303-
304-
const auto maxByteCount = sizeof(decimalValue);
318+
const auto maxByteCount = sizeof(TUnderlyingValueType);
305319
char* buffer = BufferForStringLikeValues_->Preallocate(maxByteCount);
306-
TStringBuf decimalBinary;
307-
if constexpr (std::is_same_v<TUnderlyingValueType, TDecimal::TValue128>) {
308-
decimalBinary = TDecimal::WriteBinary128Variadic(precision, decimalValue, buffer, maxByteCount);
309-
} else if constexpr (std::is_same_v<TUnderlyingValueType, TDecimal::TValue256>) {
310-
decimalBinary = TDecimal::WriteBinary256(precision, decimalValue, buffer, maxByteCount);
311-
} else {
312-
static_assert(std::is_same_v<TUnderlyingValueType, TDecimal::TValue256>, "Unexpected decimal type");
313-
}
320+
auto decimalBinary = SerializeDecimalBinary<TUnderlyingValueType>(arrowValue, precision, buffer, maxByteCount);
314321
BufferForStringLikeValues_->Advance(decimalBinary.size());
315-
316322
return MakeUnversionedStringValue(decimalBinary, columnId);
317323
}
318324
};
@@ -456,6 +462,20 @@ class TArrayCompositeVisitor
456462
return ParseStruct();
457463
}
458464

465+
arrow::Status Visit(const arrow::Decimal128Type& type) override
466+
{
467+
return ParseStringLikeArray<arrow::Decimal128Array>([&] (const TStringBuf& value) {
468+
WriteDecimalBinary<TDecimal::TValue128>(value, type.precision());
469+
});
470+
}
471+
472+
arrow::Status Visit(const arrow::Decimal256Type& type) override
473+
{
474+
return ParseStringLikeArray<arrow::Decimal256Array>([&] (const TStringBuf& value) {
475+
WriteDecimalBinary<TDecimal::TValue256>(value, type.precision());
476+
});
477+
}
478+
459479
private:
460480
const int RowIndex_;
461481

@@ -505,13 +525,21 @@ class TArrayCompositeVisitor
505525

506526
template <typename ArrayType>
507527
arrow::Status ParseStringLikeArray()
528+
{
529+
return ParseStringLikeArray<ArrayType>([&] (const TStringBuf& value) {
530+
Writer_->WriteBinaryString(value);
531+
});
532+
}
533+
534+
template <typename ArrayType>
535+
arrow::Status ParseStringLikeArray(auto writeStringValue)
508536
{
509537
auto array = std::static_pointer_cast<ArrayType>(Array_);
510538
if (array->IsNull(RowIndex_)) {
511539
Writer_->WriteEntity();
512540
} else {
513541
auto element = array->GetView(RowIndex_);
514-
Writer_->WriteBinaryString(TStringBuf(element.data(), element.size()));
542+
writeStringValue(TStringBuf(element.data(), element.size()));
515543
}
516544
return arrow::Status::OK();
517545
}
@@ -610,6 +638,15 @@ class TArrayCompositeVisitor
610638
}
611639
return arrow::Status::OK();
612640
}
641+
642+
template <class TUnderlyingType>
643+
void WriteDecimalBinary(TStringBuf arrowValue, int precision)
644+
{
645+
const auto maxByteCount = sizeof(TUnderlyingType);
646+
char buffer[maxByteCount];
647+
auto decimalBinary = SerializeDecimalBinary<TUnderlyingType>(arrowValue, precision, buffer, maxByteCount);
648+
Writer_->WriteBinaryString(decimalBinary);
649+
}
613650
};
614651

615652
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)