1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-08-26 05:02:32 +03:00

MCOL-4387 Convert dataconvert::decimalToString() into VDecimal and TSInt128 methods

This commit is contained in:
Roman Nozdrin
2020-11-10 17:27:16 +00:00
parent 007b8a5082
commit 58495d0d2f
29 changed files with 793 additions and 878 deletions

View File

@@ -632,16 +632,20 @@ int TypeHandlerSLongDouble::storeValueToField(rowgroup::Row &row, int pos,
int TypeHandlerXDecimal::storeValueToField64(rowgroup::Row &row, int pos,
StoreField *f) const
{
int64_t val = row.getIntField(pos);
return f->store_decimal64(val);
return f->store_decimal64(datatypes::VDecimal(row.getIntField(pos),
f->scale(),
f->precision()));
}
int TypeHandlerXDecimal::storeValueToField128(rowgroup::Row &row, int pos,
StoreField *f) const
{
int128_t* dec= row.getBinaryField<int128_t>(pos);
return f->store_decimal128(*dec);
int128_t* decPtr = row.getBinaryField<int128_t>(pos);
return f->store_decimal128(datatypes::VDecimal(0,
f->scale(),
f->precision(),
decPtr));
}
@@ -780,12 +784,8 @@ TypeHandlerXDecimal::format128(const SimpleValue &v,
const
{
idbassert(isValidXDecimal128(attr));
ostringstream oss;
char buf[datatypes::Decimal::MAXLENGTH16BYTES];
int128_t tmp= v.toSInt128();
DataConvert::decimalToString(&tmp, (unsigned) attr.scale, buf, (uint8_t) sizeof(buf), code());
oss << buf;
return oss.str();
datatypes::VDecimal dec(0, attr.scale, attr.precision, v.toSInt128());
return dec.toString(true);
}

View File

@@ -720,6 +720,7 @@ public:
virtual ~StoreField() {}
virtual int32_t colWidth() const = 0;
virtual int32_t precision() const = 0;
virtual int32_t scale() const = 0;
virtual int store_date(int64_t val) = 0;
virtual int store_datetime(int64_t val) = 0;
virtual int store_time(int64_t val) = 0;
@@ -730,8 +731,8 @@ public:
virtual int store_float(float val) = 0;
virtual int store_double(double val) = 0;
virtual int store_long_double(long double val) = 0;
virtual int store_decimal64(int64_t val) = 0;
virtual int store_decimal128(const int128_t &val) = 0;
virtual int store_decimal64(const datatypes::VDecimal& dec) = 0;
virtual int store_decimal128(const datatypes::VDecimal& dec) = 0;
virtual int store_lob(const char *str, size_t length) = 0;
};

View File

@@ -195,45 +195,6 @@ namespace datatypes
}
}
std::string Decimal::toString(VDecimal& value)
{
char buf[Decimal::MAXLENGTH16BYTES];
if (value.s128Value == Decimal128Null)
{
return std::string("NULL");
}
else if (value.s128Value == Decimal128Empty)
{
return std::string("EMPTY");
}
dataconvert::DataConvert::decimalToString(&value.s128Value,
value.scale, buf, (uint8_t) sizeof(buf),
datatypes::SystemCatalog::DECIMAL);
return std::string(buf);
}
std::string Decimal::toString(const VDecimal& value)
{
return toString(const_cast<VDecimal&>(value));
}
std::string Decimal::toString(const int128_t& value)
{
char buf[Decimal::MAXLENGTH16BYTES];
if (value == Decimal128Null)
{
return std::string("NULL");
}
else if (value == Decimal128Empty)
{
return std::string("EMPTY");
}
int128_t& constLessValue = const_cast<int128_t&>(value);
dataconvert::DataConvert::decimalToString(&constLessValue,
0, buf, sizeof(buf), datatypes::SystemCatalog::DECIMAL);
return std::string(buf);
}
int Decimal::compare(const VDecimal& l, const VDecimal& r)
{
int128_t divisorL, divisorR;
@@ -559,4 +520,221 @@ namespace datatypes
}
}
// Writes integer part of a Decimal using int128 argument provided
uint8_t VDecimal::writeIntPart(const int128_t& x,
char* buf,
const uint8_t buflen) const
{
char* p = buf;
int128_t intPart = x;
int128_t high = 0, mid = 0, low = 0;
uint64_t maxUint64divisor = 10000000000000000000ULL;
// Assuming scale = [0, 56]
switch (scale / datatypes::maxPowOf10)
{
case 2: // scale = [38, 56]
intPart /= datatypes::mcs_pow_10[datatypes::maxPowOf10];
intPart /= datatypes::mcs_pow_10[datatypes::maxPowOf10];
low = intPart;
break;
case 1: // scale = [19, 37]
intPart /= datatypes::mcs_pow_10[datatypes::maxPowOf10];
intPart /= datatypes::mcs_pow_10[scale % datatypes::maxPowOf10];
low = intPart % maxUint64divisor;
mid = intPart / maxUint64divisor;
break;
case 0: // scale = [0, 18]
intPart /= datatypes::mcs_pow_10[scale % datatypes::maxPowOf10];
low = intPart % maxUint64divisor;
intPart /= maxUint64divisor;
mid = intPart % maxUint64divisor;
high = intPart / maxUint64divisor;
break;
default:
throw logging::QueryDataExcept("VDecimal::writeIntPart() bad scale",
logging::formatErr);
}
p += printPodParts(p, high, mid, low);
uint8_t written = p - buf;
if (buflen <= written)
{
throw logging::QueryDataExcept("VDecimal::writeIntPart() char buffer overflow.",
logging::formatErr);
}
return written;
}
uint8_t VDecimal::writeFractionalPart(const int128_t& x,
char* buf,
const uint8_t buflen) const
{
int128_t scaleDivisor = 1;
char* p = buf;
switch (scale / datatypes::maxPowOf10)
{
case 2:
scaleDivisor *= datatypes::mcs_pow_10[datatypes::maxPowOf10];
scaleDivisor *= datatypes::mcs_pow_10[datatypes::maxPowOf10];
break;
case 1:
scaleDivisor *= datatypes::mcs_pow_10[datatypes::maxPowOf10];
//fallthrough
case 0:
scaleDivisor *= datatypes::mcs_pow_10[scale % datatypes::maxPowOf10];
}
int128_t fractionalPart = x % scaleDivisor;
// divide by the base until we have non-zero quotient
scaleDivisor /= 10;
while (scaleDivisor > 1 && fractionalPart / scaleDivisor == 0)
{
*p++ = '0';
scaleDivisor /= 10;
}
size_t written = p - buf;;
p += TSInt128::writeIntPart(fractionalPart, p, buflen - written);
return p - buf;
}
// The method writes Decimal based on TSInt128 with scale provided.
// It first writes sign, then extracts integer part
// prints delimiter and then decimal part.
std::string VDecimal::toStringTSInt128WithScale() const
{
char buf[Decimal::MAXLENGTH16BYTES];
uint8_t left = sizeof(buf);
char* p = buf;
int128_t tempValue = s128Value;
// sign
if (tempValue < static_cast<int128_t>(0))
{
*p++ = '-';
tempValue *= -1;
left--;
}
// integer part
p += writeIntPart(tempValue, p, left);
// decimal delimiter
*p++ = '.';
// decimal part
left = sizeof(buf) - (p - buf);
p += writeFractionalPart(tempValue, p, left);
*p = '\0';
uint8_t written = p - buf;
if (sizeof(buf) <= written)
{
throw logging::QueryDataExcept("VDecimal::toString() char buffer overflow.",
logging::formatErr);
}
return std::string(buf);
}
std::string VDecimal::toStringTSInt64() const
{
char buf[Decimal::MAXLENGTH8BYTES];
// Need 19 digits maxium to hold a sum result of 18 digits decimal column.
// We don't make a copy of value b/c we mutate its string
// representation.
#ifndef __LP64__
snprintf(buf, sizeof(buf), "%lld", value);
#else
snprintf(buf, sizeof(buf), "%ld", value);
#endif
//we want to move the last dt_scale chars right by one spot
// to insert the dp we want to move the trailing null as well,
// so it's really dt_scale+1 chars
size_t l1 = strlen(buf);
char* ptr = &buf[0];
if (value < 0)
{
ptr++;
idbassert(l1 >= 2);
l1--;
}
//need to make sure we have enough leading zeros for this to work.
//at this point scale is always > 0
size_t l2 = 1;
if ((unsigned)scale > l1)
{
const char* zeros = "00000000000000000000"; //20 0's
size_t diff = 0;
if (value != 0)
diff = scale - l1; //this will always be > 0
else
diff = scale;
memmove((ptr + diff), ptr, l1 + 1); //also move null
memcpy(ptr, zeros, diff);
if (value != 0)
l1 = 0;
else
l1 = 1;
}
else if ((unsigned)scale == l1)
{
l1 = 0;
l2 = 2;
}
else
{
l1 -= scale;
}
memmove((ptr + l1 + l2), (ptr + l1), scale + 1); //also move null
if (l2 == 2)
*(ptr + l1++) = '0';
*(ptr + l1) = '.';
return std::string(buf);
}
// Dispatcher method for toString() implementations
std::string VDecimal::toString(bool hasTSInt128) const
{
// There must be no empty at this point though
if (isNull())
{
return std::string("NULL");
}
if(LIKELY(hasTSInt128 || isTSInt128ByPrecision()))
{
if (scale)
{
return toStringTSInt128WithScale();
}
return TSInt128::toString();
}
// TSInt64 Decimal
if (scale)
{
return toStringTSInt64();
}
return std::to_string(value);
}
std::ostream& operator<<(std::ostream& os, const VDecimal& dec)
{
os << dec.toString();
return os;
}
} // end of namespace

View File

@@ -131,8 +131,8 @@ const int128_t mcs_pow_10_128[20] =
};
constexpr uint32_t maxPowOf10 = sizeof(mcs_pow_10)/sizeof(mcs_pow_10[0])-1;
constexpr int128_t Decimal128Null = int128_t(0x8000000000000000LL) << 64;
constexpr int128_t Decimal128Empty = (int128_t(0x8000000000000000LL) << 64) + 1;
constexpr int128_t Decimal128Null = TSInt128::NullValue;
constexpr int128_t Decimal128Empty = TSInt128::EmptyValue;
/**
@@ -172,7 +172,7 @@ class Decimal
Decimal() { };
~Decimal() { };
static constexpr uint8_t MAXLENGTH16BYTES = 42;
static constexpr uint8_t MAXLENGTH16BYTES = TSInt128::maxLength();
static constexpr uint8_t MAXLENGTH8BYTES = 23;
static inline bool isWideDecimalNullValue(const int128_t& val)
@@ -259,13 +259,6 @@ class Decimal
const VDecimal& r,
VDecimal& result);
/**
@brief Convenience methods to put decimal into a std::string.
*/
static std::string toString(VDecimal& value);
static std::string toString(const VDecimal& value);
static std::string toString(const int128_t& value);
/**
@brief The method detects whether decimal type is wide
using precision.
@@ -558,6 +551,14 @@ class VDecimal: public TSInt128
precision(p)
{ }
VDecimal(int64_t unused, int8_t s, uint8_t p, const int128_t* val128Ptr) :
TSInt128(val128Ptr),
value(unused),
scale(s),
precision(p)
{ }
int decimalComp(const VDecimal& d) const
{
lldiv_t d1 = lldiv(value, static_cast<int64_t>(mcs_pow_10[scale]));
@@ -819,9 +820,35 @@ class VDecimal: public TSInt128
}
}
inline bool isTSInt128ByPrecision() const
{
return precision > INT64MAXPRECISION
&& precision <= INT128MAXPRECISION;
}
// hasTSInt128 explicitly tells to print int128 out in cases
// where precision can't detect decimal type properly, e.g.
// DECIMAL(10)/DECIMAL(38)
std::string toString(bool hasTSInt128 = false) const;
friend std::ostream& operator<<(std::ostream& os, const VDecimal& dec);
int64_t value;
int8_t scale; // 0~38
uint8_t precision; // 1~38
// STRICTLY for unit tests!!!
void setTSInt64Value(const int64_t x) { value = x; }
void setTSInt128Value(const int128_t& x) { s128Value = x; }
private:
uint8_t writeIntPart(const int128_t& x,
char* buf,
const uint8_t buflen) const;
uint8_t writeFractionalPart(const int128_t& x,
char* buf,
const uint8_t buflen) const;
std::string toStringTSInt128WithScale() const;
std::string toStringTSInt64() const;
};
} //end of namespace

View File

@@ -17,7 +17,10 @@
MA 02110-1301, USA.
*/
#include <iostream>
#include "mcs_int128.h"
#include "exceptclasses.h"
namespace datatypes
{
@@ -33,6 +36,99 @@ namespace datatypes
return getLongDoubleFromFloat128(static_cast<__float128>(s128Value));
}
uint8_t TSInt128::printPodParts(char* buf,
const int128_t& high,
const int128_t& mid,
const int128_t& low) const
{
char* p = buf;
// pod[0] is low 8 bytes, pod[1] is high 8 bytes
const uint64_t* high_pod = reinterpret_cast<const uint64_t*>(&high);
const uint64_t* mid_pod = reinterpret_cast<const uint64_t*>(&mid);
const uint64_t* low_pod = reinterpret_cast<const uint64_t*>(&low);
if (high_pod[0] != 0)
{
p += sprintf(p, "%lu", high_pod[0]);
p += sprintf(p, "%019lu", mid_pod[0]);
p += sprintf(p, "%019lu", low_pod[0]);
}
else if (mid_pod[0] != 0)
{
p += sprintf(p, "%lu", mid_pod[0]);
p += sprintf(p, "%019lu", low_pod[0]);
}
else
{
p += sprintf(p, "%lu", low_pod[0]);
}
return p - buf;
}
// This method writes unsigned integer representation of TSInt128
uint8_t TSInt128::writeIntPart(const int128_t& x,
char* buf,
const uint8_t buflen) const
{
char* p = buf;
int128_t high = 0, mid = 0, low = 0;
uint64_t maxUint64divisor = 10000000000000000000ULL;
low = x % maxUint64divisor;
int128_t value = x / maxUint64divisor;
mid = value % maxUint64divisor;
high = value / maxUint64divisor;
p += printPodParts(p, high, mid, low);
uint8_t written = p - buf;
if (buflen <= written)
{
throw logging::QueryDataExcept("TSInt128::writeIntPart() char buffer overflow.",
logging::formatErr);
}
return written;
}
// conversion to std::string
std::string TSInt128::toString() const
{
if (isNull())
{
return std::string("NULL");
}
if (isEmpty())
{
return std::string("EMPTY");
}
int128_t tempValue = s128Value;
char buf[TSInt128::MAXLENGTH16BYTES];
uint8_t left = sizeof(buf);
char* p = buf;
// sign
if (tempValue < static_cast<int128_t>(0))
{
*p++ = '-';
tempValue *= -1;
left--;
}
// integer part
// reduce the size by one to account for \0
left--;
p += writeIntPart(tempValue, p, left);
*p = '\0';
return std::string(buf);
}
std::ostream& operator<<(std::ostream& os, const TSInt128& x)
{
os << x.toString();
return os;
}
// The method converts a wide decimal s128Value to an int64_t,
// saturating the s128Value if necessary.
inline int64_t TSInt128::getInt64FromWideDecimal()

View File

@@ -23,6 +23,7 @@
#include <cstdint>
#include <limits>
#include <type_traits>
#include <string>
// Inline asm has three argument lists: output, input and clobber list
#if defined(__GNUC__) && (__GNUC___ > 7)
@@ -116,18 +117,41 @@ static inline long double getLongDoubleFromFloat128(const __float128& value)
class TSInt128
{
public:
// A variety of ctors for aligned and unaligned arguments
static constexpr uint8_t MAXLENGTH16BYTES = 42;
static constexpr int128_t NullValue = int128_t(0x8000000000000000LL) << 64;
static constexpr int128_t EmptyValue = (int128_t(0x8000000000000000LL) << 64) + 1;
// A variety of ctors for aligned and unaligned arguments
TSInt128(): s128Value(0) { }
// aligned argument
// aligned argument
TSInt128(const int128_t& x) { s128Value = x; }
// unaligned argument
// unaligned argument
TSInt128(const int128_t* x) { assignPtrPtr(&s128Value, x); }
// unaligned argument
// unaligned argument
TSInt128(const unsigned char* x) { assignPtrPtr(&s128Value, x); }
// Method returns max length of a string representation
static constexpr uint8_t maxLength()
{
return TSInt128::MAXLENGTH16BYTES;
}
// Checks if the value is NULL
inline bool isNull() const
{
return s128Value == NullValue;
}
// Checks if the value is Empty
inline bool isEmpty() const
{
return s128Value == EmptyValue;
}
// The method copies 16 bytes from one memory cell
// into another using memcpy or SIMD.
// memcpy in gcc >= 7 is replaced with SIMD instructions
@@ -158,6 +182,22 @@ class TSInt128
return s128Value == static_cast<int128_t>(x);
}
// print int128_t parts represented as PODs
uint8_t printPodParts(char* buf,
const int128_t& high,
const int128_t& mid,
const int128_t& low) const;
// writes integer part of dec into a buffer
uint8_t writeIntPart(const int128_t& x,
char* buf,
const uint8_t buflen) const;
// string representation of TSInt128
std::string toString() const;
friend std::ostream& operator<<(std::ostream& os, const TSInt128& x);
// The method converts a wide decimal s128Value to a double.
inline double getDoubleFromWideDecimal();