8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

High precision datatype support - Numeric(34,x) (#108)

* Raise underflow when close to 0 decfloat value casted to double
* High precision NUMERIC datatype based on DECFLOAT
* Fixed bulk insert mode in isql for decfloat values
* Enforce correct decQuad format after arithmetic operations
* Minimum docs for high precision NUMERIC/DECIMAL

* Some fixes of code suggested by Adriano:
- Use "const" keyword for Decimal128 constants declared internally
- Remove unneeded buffer initialization
- Remove unused function makeDecimalFixed()
- Follow firebird naming conventions to make code better readable and avoid possible conflicts with various .h files
- Added forgotten scale for DecimalFixed
This commit is contained in:
Alexander Peshkov 2017-10-03 17:06:08 +03:00 committed by GitHub
parent f0154f8822
commit 6198bc8ee1
46 changed files with 1495 additions and 250 deletions

View File

@ -185,3 +185,33 @@ DECFLOAT (FB 4.0)
as a literal, instead you can use the equivalent in scientific notation: 1.1E-1022.
Similarly 10<1022 zeroes>0 can be presented as 1.0E1024.
Enhancement in precision of calculations with NUMERIC/DECIMAL (FB 4.0)
--------------
Function:
Maximum precision of NUMERIC and DECIMAL data types is increased to 34 digits.
Author:
Alex Peshkoff <peshkoff@mail.ru>
Syntax rules:
NUMERIC ( P {, N} )
DECIMAL ( P {, N} )
where P is precision (P <= 34, was limited prior with 18 digits) and N is optional number
of digits after decimal separator (as before).
Storage:
128-bit, format according to IEEE 754.
Example(s):
1. DECLARE VARIABLE VAR1 DECIMAL(25);
2. CREATE TABLE TABLE1 (FIELD1 NUMERIC(34, 17));
Note(s):
Numerics with precision less than 19 digits use SMALLINT, INTEGER, BIGINT or DOUBLE PRECISION
as base datatype depending upon number of digits and dialect. When precision is between 19 and
34 digits DECFLOAT(34) is used for it. Actual precision is always increased to 34 digits. For
complex calculations such digits are casted (internally, in trivial way) to DECFLOAT(34) and
the result of various math (log, exp, etc.) and aggregate functions using high precision
numeric argument is DECFLOAT(34).

View File

@ -163,6 +163,7 @@ ULONG CAN_encode_decode(burp_rel* relation, lstring* buffer, UCHAR* data, bool_t
break;
case dtype_dec128:
case dtype_dec_fixed:
if (!xdr_dec128(xdrs, (Firebird::Decimal128*) p))
return FALSE;
break;

View File

@ -71,7 +71,7 @@ public:
init(DEC_INIT_DECIMAL64);
}
DecimalContext(const Decimal128*, DecimalStatus ds)
DecimalContext(const Decimal128Base*, DecimalStatus ds)
: decSt(ds)
{
init(DEC_INIT_DECIMAL128);
@ -116,8 +116,10 @@ private:
}
};
CDecimal128 dmax(DBL_MAX, DecimalStatus(0)), dmin(-DBL_MAX, DecimalStatus(0));
CDecimal128 i64max(MAX_SINT64, DecimalStatus(0)), i64min(MIN_SINT64, DecimalStatus(0));
const CDecimal128 dmax(DBL_MAX, DecimalStatus(0)), dmin(-DBL_MAX, DecimalStatus(0));
const CDecimal128 dzup(DBL_MIN, DecimalStatus(0)), dzlw(-DBL_MIN, DecimalStatus(0));
const CDecimal128 i64max(MAX_SINT64, DecimalStatus(0)), i64min(MIN_SINT64, DecimalStatus(0));
const CDecimal128 c1(1);
unsigned digits(const unsigned pMax, unsigned char* const coeff, int& exp)
{
@ -257,6 +259,15 @@ Decimal64 Decimal64::set(SLONG value, DecimalStatus decSt, int scale)
return *this;
}
Decimal64 Decimal64::set(DecimalFixed value, DecimalStatus decSt, int scale)
{
Decimal128 tmp;
tmp.set(value, decSt, scale);
*this = tmp.toDecimal64(decSt);
return *this;
}
Decimal64 Decimal64::set(SINT64 value, DecimalStatus decSt, int scale)
{
{
@ -499,6 +510,14 @@ Decimal128 Decimal128::set(SLONG value, DecimalStatus decSt, int scale)
return *this;
}
Decimal128 Decimal128::set(DecimalFixed value, DecimalStatus decSt, int scale)
{
*this = value;
setScale(decSt, -scale);
return *this;
}
Decimal128 Decimal128::set(SINT64 value, DecimalStatus decSt, int scale)
{
{
@ -538,6 +557,62 @@ Decimal128 Decimal128::set(double value, DecimalStatus decSt)
return *this;
}
DecimalFixed DecimalFixed::set(SLONG value)
{
decQuadFromInt32(&dec, value);
return *this;
}
DecimalFixed DecimalFixed::set(SINT64 value)
{
int high = value >> 32;
unsigned low = value & 0xFFFFFFFF;
DecimalContext context(this, DecimalStatus(0));
decQuad pow2_32;
decQuadFromString(&pow2_32, "4294967296", &context);
decQuad up, down;
decQuadFromInt32(&up, high);
decQuadFromUInt32(&down, low);
decQuadFMA(&dec, &up, &pow2_32, &down, &context);
return *this;
}
DecimalFixed DecimalFixed::set(const char* value, int scale, DecimalStatus decSt)
{
{ // scope for 'context'
DecimalContext context(this, decSt);
decQuadFromString(&dec, value, &context);
}
exactInt(decSt, scale);
return *this;
}
DecimalFixed DecimalFixed::set(double value, int scale, DecimalStatus decSt)
{
char s[50];
sprintf(s, "%18.016e", value);
{ // scope for 'context'
DecimalContext context(this, decSt);
decQuadFromString(&dec, s, &context);
}
exactInt(decSt, scale);
return *this;
}
void DecimalFixed::exactInt(DecimalStatus decSt, int scale)
{
setScale(decSt, -scale);
DecimalContext context(this, decSt);
decQuadToIntegralExact(&dec, &dec, &context);
decQuadQuantize(&dec, &dec, &c1.dec, &context);
}
Decimal128 Decimal128::operator=(Decimal64 d64)
{
decDoubleToWider(&d64.dec, &dec);
@ -553,6 +628,13 @@ int Decimal128::toInteger(DecimalStatus decSt, int scale) const
return decQuadToInt32(&tmp.dec, &context, rMode);
}
int DecimalFixed::toInteger(DecimalStatus decSt) const
{
DecimalContext context(this, decSt);
enum rounding rMode = decContextGetRounding(&context);
return decQuadToInt32(&dec, &context, rMode);
}
void Decimal128::toString(DecimalStatus decSt, unsigned length, char* to) const
{
DecimalContext context(this, decSt);
@ -582,16 +664,37 @@ void Decimal128::toString(string& to) const
to.recalculate_length();
}
double Decimal128::toDouble(DecimalStatus decSt) const
Decimal128 DecimalFixed::scaled128(DecimalStatus decSt, int scale) const
{
Decimal128 tmp;
tmp.set(*this, decSt, -scale);
return tmp;
}
void DecimalFixed::toString(DecimalStatus decSt, int scale, unsigned length, char* to) const
{
scaled128(decSt, scale).toString(decSt, length, to);
}
void DecimalFixed::toString(DecimalStatus decSt, int scale, string& to) const
{
scaled128(decSt, scale).toString(to);
}
double Decimal128Base::toDouble(DecimalStatus decSt) const
{
DecimalContext context(this, decSt);
if (compare(decSt, dmin) < 0 || compare(decSt, dmax) > 0)
decContextSetStatus(&context, DEC_Overflow);
else if ((!decQuadIsZero(&dec)) && compare(decSt, dzlw) > 0 && compare(decSt, dzup) < 0)
{
decContextSetStatus(&context, DEC_Underflow);
return 0.0;
}
else
{
char s[IDecFloat34::STRING_SIZE];
memset(s, 0, sizeof(s));
decQuadToString(&dec, s);
return atof(s);
}
@ -630,12 +733,37 @@ SINT64 Decimal128::toInt64(DecimalStatus decSt, int scale) const
return rc;
}
UCHAR* Decimal128::getBytes()
SINT64 DecimalFixed::toInt64(DecimalStatus decSt) const
{
if (compare(decSt, i64min) < 0 || compare(decSt, i64max) > 0)
{
DecimalContext context(this, decSt);
decContextSetStatus(&context, DEC_Invalid_operation);
return 0; // in case of no trap on invalid operation
}
unsigned char coeff[DECQUAD_Pmax];
int sign = decQuadGetCoefficient(&dec, coeff);
SINT64 rc = 0;
for (int i = 0; i < DECQUAD_Pmax; ++i)
{
rc *= 10;
if (sign)
rc -= coeff[i];
else
rc += coeff[i];
}
return rc;
}
UCHAR* Decimal128Base::getBytes()
{
return dec.bytes;
}
Decimal64 Decimal128::toDecimal64(DecimalStatus decSt) const
Decimal64 Decimal128Base::toDecimal64(DecimalStatus decSt) const
{
Decimal64 rc;
DecimalContext context(this, decSt);
@ -643,7 +771,7 @@ Decimal64 Decimal128::toDecimal64(DecimalStatus decSt) const
return rc;
}
void Decimal128::setScale(DecimalStatus decSt, int scale)
void Decimal128Base::setScale(DecimalStatus decSt, int scale)
{
if (scale)
{
@ -653,7 +781,7 @@ void Decimal128::setScale(DecimalStatus decSt, int scale)
}
}
int Decimal128::compare(DecimalStatus decSt, Decimal128 tgt) const
int Decimal128Base::compare(DecimalStatus decSt, Decimal128Base tgt) const
{
DecimalContext context(this, decSt);
decQuad r;
@ -661,7 +789,7 @@ int Decimal128::compare(DecimalStatus decSt, Decimal128 tgt) const
return decQuadToInt32(&r, &context, DEC_ROUND_HALF_UP);
}
bool Decimal128::isInf() const
bool Decimal128Base::isInf() const
{
switch(decQuadClass(&dec))
{
@ -673,7 +801,7 @@ bool Decimal128::isInf() const
return false;
}
bool Decimal128::isNan() const
bool Decimal128Base::isNan() const
{
switch(decQuadClass(&dec))
{
@ -685,7 +813,7 @@ bool Decimal128::isNan() const
return false;
}
int Decimal128::sign() const
int Decimal128Base::sign() const
{
if (decQuadIsZero(&dec))
return 0;
@ -694,13 +822,6 @@ int Decimal128::sign() const
return 1;
}
Decimal128 Decimal128::abs() const
{
Decimal128 rc;
decQuadCopyAbs(&rc.dec, &dec);
return rc;
}
Decimal128 Decimal128::ceil(DecimalStatus decSt) const
{
DecimalContext context(this, decSt);
@ -718,13 +839,27 @@ Decimal128 Decimal128::floor(DecimalStatus decSt) const
}
#ifdef DEV_BUILD
int Decimal128::show()
int Decimal128Base::show()
{
decQuadShow(&dec, "");
return 0;
}
#endif
Decimal128 Decimal128::abs() const
{
Decimal128 rc;
decQuadCopyAbs(&rc.dec, &dec);
return rc;
}
Decimal128 Decimal128::neg() const
{
Decimal128 rc;
decQuadCopyNegate(&rc.dec, &dec);
return rc;
}
Decimal128 Decimal128::add(DecimalStatus decSt, Decimal128 op2) const
{
DecimalContext context(this, decSt);
@ -749,6 +884,50 @@ Decimal128 Decimal128::mul(DecimalStatus decSt, Decimal128 op2) const
return rc;
}
DecimalFixed DecimalFixed::abs() const
{
DecimalFixed rc;
decQuadCopyAbs(&rc.dec, &dec);
return rc;
}
DecimalFixed DecimalFixed::neg() const
{
DecimalFixed rc;
decQuadCopyNegate(&rc.dec, &dec);
return rc;
}
DecimalFixed DecimalFixed::add(DecimalStatus decSt, DecimalFixed op2) const
{
DecimalContext context(this, decSt);
DecimalFixed rc;
decQuadAdd(&rc.dec, &dec, &op2.dec, &context);
context.checkForExceptions();
decQuadQuantize(&rc.dec, &rc.dec, &c1.dec, &context);
return rc;
}
DecimalFixed DecimalFixed::sub(DecimalStatus decSt, DecimalFixed op2) const
{
DecimalContext context(this, decSt);
DecimalFixed rc;
decQuadSubtract(&rc.dec, &dec, &op2.dec, &context);
context.checkForExceptions();
decQuadQuantize(&rc.dec, &rc.dec, &c1.dec, &context);
return rc;
}
DecimalFixed DecimalFixed::mul(DecimalStatus decSt, DecimalFixed op2) const
{
DecimalContext context(this, decSt);
DecimalFixed rc;
decQuadMultiply(&rc.dec, &dec, &op2.dec, &context);
context.checkForExceptions();
decQuadQuantize(&rc.dec, &rc.dec, &c1.dec, &context);
return rc;
}
Decimal128 Decimal128::div(DecimalStatus decSt, Decimal128 op2) const
{
DecimalContext context(this, decSt);
@ -757,10 +936,24 @@ Decimal128 Decimal128::div(DecimalStatus decSt, Decimal128 op2) const
return rc;
}
Decimal128 Decimal128::neg() const
DecimalFixed DecimalFixed::div(DecimalStatus decSt, DecimalFixed op2, int scale) const
{
Decimal128 rc;
decQuadCopyNegate(&rc.dec, &dec);
DecimalContext context(this, decSt);
DecimalFixed rc;
// first divide with full decfloat precision
decQuadDivide(&rc.dec, &dec, &op2.dec, &context);
// next re-scale & int-ize
rc.exactInt(decSt, scale);
return rc;
}
DecimalFixed DecimalFixed::mod(DecimalStatus decSt, DecimalFixed op2) const
{
DecimalContext context(this, decSt);
DecimalFixed rc;
decQuadRemainder(&rc.dec, &dec, &op2.dec, &context);
return rc;
}
@ -825,7 +1018,7 @@ Decimal128 Decimal128::log10(DecimalStatus decSt) const
return rc;
}
void Decimal128::makeKey(ULONG* key) const
void Decimal128Base::makeKey(ULONG* key) const
{
unsigned char coeff[DECQUAD_Pmax];
int sign = decQuadGetCoefficient(&dec, coeff);
@ -834,7 +1027,7 @@ void Decimal128::makeKey(ULONG* key) const
make(key, DECQUAD_Pmax, DECQUAD_Bias, sizeof(dec), coeff, sign, exp);
}
void Decimal128::grabKey(ULONG* key)
void Decimal128Base::grabKey(ULONG* key)
{
int exp, sign;
unsigned char bcd[DECQUAD_Pmax];
@ -844,12 +1037,12 @@ void Decimal128::grabKey(ULONG* key)
decQuadFromBCD(&dec, exp, bcd, sign);
}
ULONG Decimal128::getIndexKeyLength()
ULONG Decimal128Base::getIndexKeyLength()
{
return 17;
}
ULONG Decimal128::makeIndexKey(vary* buf)
ULONG Decimal128Base::makeIndexKey(vary* buf)
{
unsigned char coeff[DECQUAD_Pmax + 2];
int sign = decQuadGetCoefficient(&dec, coeff);

View File

@ -32,6 +32,8 @@
#include "firebird/Interface.h"
#include "fb_exception.h"
#include <string.h>
#include "classes/fb_string.h"
extern "C"
@ -63,9 +65,13 @@ struct DecimalBinding
SCHAR numScale;
};
class DecimalFixed;
class Decimal64
{
friend class Decimal128;
friend class DecimalFixed;
friend class Decimal128Base;
public:
#if SIZEOF_LONG < 8
@ -75,6 +81,7 @@ public:
Decimal64 set(SINT64 value, DecimalStatus decSt, int scale);
Decimal64 set(const char* value, DecimalStatus decSt);
Decimal64 set(double value, DecimalStatus decSt);
Decimal64 set(DecimalFixed value, DecimalStatus decSt, int scale);
UCHAR* getBytes();
Decimal64 abs() const;
@ -103,12 +110,43 @@ public:
#endif
private:
decDouble dec;
void setScale(DecimalStatus decSt, int scale);
decDouble dec;
};
class Decimal128
class Decimal128Base
{
friend class Decimal128;
friend class DecimalFixed;
public:
double toDouble(DecimalStatus decSt) const;
Decimal64 toDecimal64(DecimalStatus decSt) const;
UCHAR* getBytes();
int compare(DecimalStatus decSt, Decimal128Base tgt) const;
bool isInf() const;
bool isNan() const;
int sign() const;
void makeKey(ULONG* key) const;
void grabKey(ULONG* key);
static ULONG getIndexKeyLength();
ULONG makeIndexKey(vary* buf);
#ifdef DEV_BUILD
int show();
#endif
private:
void setScale(DecimalStatus decSt, int scale);
decQuad dec;
};
class Decimal128 : public Decimal128Base
{
friend class Decimal64;
@ -121,53 +159,40 @@ public:
Decimal128 set(SINT64 value, DecimalStatus decSt, int scale);
Decimal128 set(const char* value, DecimalStatus decSt);
Decimal128 set(double value, DecimalStatus decSt);
Decimal128 set(DecimalFixed value, DecimalStatus decSt, int scale);
Decimal128 operator=(Decimal64 d64);
int toInteger(DecimalStatus decSt, int scale) const;
void toString(DecimalStatus decSt, unsigned length, char* to) const;
void toString(string& to) const;
double toDouble(DecimalStatus decSt) const;
int toInteger(DecimalStatus decSt, int scale) const;
SINT64 toInt64(DecimalStatus decSt, int scale) const;
UCHAR* getBytes();
Decimal64 toDecimal64(DecimalStatus decSt) const;
Decimal128 abs() const;
Decimal128 ceil(DecimalStatus decSt) const;
Decimal128 floor(DecimalStatus decSt) const;
Decimal128 abs() const;
Decimal128 neg() const;
Decimal128 add(DecimalStatus decSt, Decimal128 op2) const;
Decimal128 sub(DecimalStatus decSt, Decimal128 op2) const;
Decimal128 mul(DecimalStatus decSt, Decimal128 op2) const;
Decimal128 div(DecimalStatus decSt, Decimal128 op2) const;
Decimal128 neg() const;
Decimal128 fma(DecimalStatus decSt, Decimal128 op2, Decimal128 op3) const;
Decimal128 sqrt(DecimalStatus decSt) const;
Decimal128 pow(DecimalStatus decSt, Decimal128 op2) const;
Decimal128 ln(DecimalStatus decSt) const;
Decimal128 log10(DecimalStatus decSt) const;
int compare(DecimalStatus decSt, Decimal128 tgt) const;
bool isInf() const;
bool isNan() const;
int sign() const;
void makeKey(ULONG* key) const;
void grabKey(ULONG* key);
static ULONG getIndexKeyLength();
ULONG makeIndexKey(vary* buf);
Decimal128 quantize(DecimalStatus decSt, Decimal128 op2) const;
Decimal128 normalize(DecimalStatus decSt) const;
short totalOrder(Decimal128 op2) const;
short decCompare(Decimal128 op2) const;
#ifdef DEV_BUILD
int show();
#endif
private:
decQuad dec;
void setScale(DecimalStatus decSt, int scale);
Decimal128 operator=(Decimal128Base d128b)
{
memcpy(&dec, &d128b.dec, sizeof(dec));
return *this;
}
};
class CDecimal128 : public Decimal128
@ -189,6 +214,45 @@ public:
}
};
class DecimalFixed : public Decimal128Base
{
public:
#if SIZEOF_LONG < 8
DecimalFixed set(int value)
{
return set(SLONG(value));
}
#endif
DecimalFixed set(SLONG value);
DecimalFixed set(SINT64 value);
DecimalFixed set(const char* value, int scale, DecimalStatus decSt);
DecimalFixed set(double value, int scale, DecimalStatus decSt);
int toInteger(DecimalStatus decSt) const;
SINT64 toInt64(DecimalStatus decSt) const;
void toString(DecimalStatus decSt, int scale, unsigned length, char* to) const;
void toString(DecimalStatus decSt, int scale, string& to) const;
DecimalFixed abs() const;
DecimalFixed neg() const;
DecimalFixed add(DecimalStatus decSt, DecimalFixed op2) const;
DecimalFixed sub(DecimalStatus decSt, DecimalFixed op2) const;
DecimalFixed mul(DecimalStatus decSt, DecimalFixed op2) const;
DecimalFixed div(DecimalStatus decSt, DecimalFixed op2, int scale) const;
DecimalFixed mod(DecimalStatus decSt, DecimalFixed op2) const;
DecimalFixed operator=(Decimal128Base d128b)
{
memcpy(&dec, &d128b.dec, sizeof(dec));
return *this;
}
void exactInt(DecimalStatus decSt, int scale); // rescale & make it integer after conversions
private:
Decimal128 scaled128(DecimalStatus decSt, int scale) const;
};
} // namespace Firebird

View File

@ -182,6 +182,12 @@ MetadataFromBlr::MetadataFromBlr(unsigned aBlrLength, const unsigned char* aBlr,
item->length = sizeof(Decimal128);
break;
case blr_dec_fixed:
item->type = SQL_DEC_FIXED;
item->length = sizeof(DecimalFixed);
item->scale = rdr.getByte();
break;
default:
(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err)

View File

@ -307,13 +307,12 @@ static void decimal_float_to_text(const dsc* from, dsc* to, DecimalStatus decSt,
try
{
Decimal128 d;
if (from->dsc_dtype == dtype_dec64)
d = *((Decimal64*) from->dsc_address);
((Decimal64*) from->dsc_address)->toString(decSt, sizeof(temp), temp);
else if (from->dsc_dtype == dtype_dec128)
((Decimal128*) from->dsc_address)->toString(decSt, sizeof(temp), temp);
else
d = *((Decimal128*) from->dsc_address);
d.toString(decSt, sizeof(temp), temp);
((DecimalFixed*) from->dsc_address)->toString(decSt, from->dsc_scale, sizeof(temp), temp);
}
catch (const Exception& ex)
{
@ -982,6 +981,10 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc
return d128.toInteger(decSt, scale);
case dtype_dec_fixed:
value = ((DecimalFixed*) p)->toInteger(decSt);
break;
case dtype_real:
case dtype_double:
if (desc->dsc_dtype == dtype_real)
@ -1182,6 +1185,10 @@ double CVT_get_double(const dsc* desc, DecimalStatus decSt, ErrorFunction err, b
return d128.toDouble(decSt);
}
case dtype_dec_fixed:
value = ((DecimalFixed*) desc->dsc_address)->toDouble(decSt);
break;
case dtype_varying:
case dtype_cstring:
case dtype_text:
@ -1470,6 +1477,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_boolean:
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
CVT_conversion_error(from, cb->err);
break;
}
@ -1505,6 +1513,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_boolean:
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
CVT_conversion_error(from, cb->err);
break;
}
@ -1540,6 +1549,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_boolean:
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
CVT_conversion_error(from, cb->err);
break;
}
@ -1729,6 +1739,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
decimal_float_to_text(from, to, decSt, cb);
return;
@ -1855,6 +1866,10 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
*((Decimal128*) p) = CVT_get_dec128(from, decSt, cb->err);
return;
case dtype_dec_fixed:
*((DecimalFixed*) p) = CVT_get_dec_fixed(from, (SSHORT) to->dsc_scale, decSt, cb->err);
return;
case dtype_boolean:
switch (from->dsc_dtype)
{
@ -1877,6 +1892,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_double:
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
CVT_conversion_error(from, cb->err);
break;
}
@ -2591,7 +2607,10 @@ Decimal64 CVT_get_dec64(const dsc* desc, DecimalStatus decSt, ErrorFunction err)
return *(Decimal64*) p;
case dtype_dec128:
return ((Decimal128*) p)->toDecimal64(decSt);
return ((Decimal128Base*) p)->toDecimal64(decSt);
case dtype_dec_fixed:
return d64.set(*((DecimalFixed*) p), decSt, scale);
default:
fb_assert(false);
@ -2672,11 +2691,14 @@ Decimal128 CVT_get_dec128(const dsc* desc, DecimalStatus decSt, ErrorFunction er
return d128.set(*((double*) p), decSt);
case dtype_dec64:
return (d128 = (*(Decimal64*) p)); // cast to higher precision never cause rounding/traps
return (d128 = *((Decimal64*) p)); // cast to higher precision never cause rounding/traps
case dtype_dec128:
return *(Decimal128*) p;
case dtype_dec_fixed:
return d128.set(*((DecimalFixed*) p), decSt, scale);
default:
fb_assert(false);
err(Arg::Gds(isc_badblk)); // internal error
@ -2695,6 +2717,104 @@ Decimal128 CVT_get_dec128(const dsc* desc, DecimalStatus decSt, ErrorFunction er
}
DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err)
{
/**************************************
*
* C V T _ g e t _ d e c 1 2 8
*
**************************************
*
* Functional description
* Convert something arbitrary to a DecFloat(34) / (128 bit).
*
**************************************/
VaryStr<1024> buffer; // represents unreasonably long decfloat literal in ASCII
DecimalFixed dfix;
Decimal128 tmp;
// adjust exact numeric values to same scaling
if (DTYPE_IS_EXACT(desc->dsc_dtype))
scale -= desc->dsc_scale;
const char* p = reinterpret_cast<char*>(desc->dsc_address);
try
{
switch (desc->dsc_dtype)
{
case dtype_short:
dfix.set(*(SSHORT*) p);
break;
case dtype_long:
dfix.set(*(SLONG*) p);
break;
case dtype_quad:
dfix.set(CVT_get_int64(desc, 0, decSt, err));
break;
case dtype_int64:
dfix.set(*(SINT64*) p);
break;
case dtype_varying:
case dtype_cstring:
case dtype_text:
CVT_make_null_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer) - 1, decSt, err);
dfix.set(buffer.vary_string, scale, decSt);
return dfix; // scale already corrected
break;
case dtype_blob:
case dtype_sql_date:
case dtype_sql_time:
case dtype_timestamp:
case dtype_array:
case dtype_dbkey:
case dtype_boolean:
CVT_conversion_error(desc, err);
break;
case dtype_real:
dfix.set(*((float*) p), scale, decSt);
return dfix; // scale already corrected
case dtype_double:
dfix.set(*((double*) p), scale, decSt);
return dfix; // scale already corrected
case dtype_dec64:
dfix = tmp = *((Decimal64*) p);
break;
case dtype_dec128:
dfix = *((Decimal128*) p);
break;
case dtype_dec_fixed:
dfix = *((DecimalFixed*) p);
break;
default:
fb_assert(false);
err(Arg::Gds(isc_badblk)); // internal error
break;
}
}
catch (const Exception& ex)
{
// reraise using passed error function
Arg::StatusVector v(ex);
err(v);
}
dfix.exactInt(decSt, scale);
return dfix;
}
SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err)
{
/**************************************
@ -2766,6 +2886,7 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
SINT64_to_SQUAD(CVT_get_int64(desc, scale, decSt, err), value);
break;
@ -2841,6 +2962,10 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFu
return d128.toInt64(decSt, scale);
}
case dtype_dec_fixed:
value = ((DecimalFixed*) p)->toInt64(decSt);
break;
case dtype_real:
case dtype_double:
if (desc->dsc_dtype == dtype_real)

View File

@ -77,6 +77,7 @@ bool CVT_get_boolean(const dsc*, ErrorFunction);
double CVT_get_double(const dsc*, Firebird::DecimalStatus, ErrorFunction, bool* getNumericOverflow = nullptr);
Firebird::Decimal64 CVT_get_dec64(const dsc*, Firebird::DecimalStatus, ErrorFunction);
Firebird::Decimal128 CVT_get_dec128(const dsc*, Firebird::DecimalStatus, ErrorFunction);
Firebird::DecimalFixed CVT_get_dec_fixed(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction);
void CVT_make_null_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction);
void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*);

File diff suppressed because it is too large Load Diff

View File

@ -60,7 +60,7 @@ inline bool DTYPE_IS_BLOB_OR_QUAD(UCHAR d)
// Exact numeric?
inline bool DTYPE_IS_EXACT(UCHAR d)
{
return d == dtype_int64 || d == dtype_long || d == dtype_short;
return d == dtype_int64 || d == dtype_long || d == dtype_short || d == dtype_dec_fixed;
}
inline bool DTYPE_IS_APPROX(UCHAR d)
@ -70,7 +70,7 @@ inline bool DTYPE_IS_APPROX(UCHAR d)
inline bool DTYPE_IS_DECFLOAT(UCHAR d)
{
return d == dtype_dec128 || d == dtype_dec64;
return d == dtype_dec128 || d == dtype_dec64 || d == dtype_dec_fixed;
}
inline bool DTYPE_IS_NUMERIC(UCHAR d)
@ -163,7 +163,12 @@ typedef struct dsc
bool isDecFloat() const
{
return DTYPE_IS_DECFLOAT(dsc_dtype);
return dsc_dtype == dtype_dec128 || dsc_dtype == dtype_dec64;
}
bool isDecFixed() const
{
return dsc_dtype == dtype_dec_fixed;
}
bool isDecOrInt() const

View File

@ -63,7 +63,8 @@
#define dtype_boolean 21
#define dtype_dec64 22
#define dtype_dec128 23
#define DTYPE_TYPE_MAX 24
#define dtype_dec_fixed 24
#define DTYPE_TYPE_MAX 25
#define ISC_TIME_SECONDS_PRECISION 10000
#define ISC_TIME_SECONDS_PRECISION_SCALE (-4)

View File

@ -855,6 +855,11 @@ static const UCHAR* sdl_desc(const UCHAR* ptr, DSC* desc)
desc->dsc_length = sizeof(Decimal128);
break;
case blr_dec_fixed:
desc->dsc_dtype = dtype_dec_fixed;
desc->dsc_length = sizeof(DecimalFixed);
break;
case blr_timestamp:
desc->dsc_dtype = dtype_timestamp;
desc->dsc_length = sizeof(ISC_QUAD);

View File

@ -1520,6 +1520,8 @@ UCHAR sqlTypeToDscType(SSHORT sqlType)
return dtype_dec64;
case SQL_DEC34:
return dtype_dec128;
case SQL_DEC_FIXED:
return dtype_dec_fixed;
default:
return dtype_unknown;
}

View File

@ -260,6 +260,7 @@ bool_t xdr_datum( XDR* xdrs, const dsc* desc, UCHAR* buffer)
break;
case dtype_dec128:
case dtype_dec_fixed:
fb_assert(desc->dsc_length >= sizeof(Firebird::Decimal128));
if (!xdr_dec128(xdrs, reinterpret_cast<Firebird::Decimal128*>(p)))
return FALSE;

View File

@ -4462,6 +4462,7 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld)
case blr_float:
case blr_dec64:
case blr_dec128:
case blr_dec_fixed:
switch (newFld.dyn_dtype)
{
case blr_blob:
@ -4587,6 +4588,23 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld)
case blr_double:
case blr_dec64:
case blr_dec128:
case blr_dec_fixed:
break;
default:
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
errorCode = isc_dyn_invalid_dtype_conversion;
break;
}
break;
case blr_dec_fixed:
switch (origFld.dyn_dtype)
{
case blr_short:
case blr_long:
case blr_int64:
case blr_dec_fixed:
break;
default:

View File

@ -467,6 +467,82 @@ void ArithmeticNode::genBlr(DsqlCompilerScratch* dsqlScratch)
GEN_expr(dsqlScratch, arg2);
}
namespace
{
const UCHAR DSC_ZTYPE_FLT64 = 0;
const UCHAR DSC_ZTYPE_FLT128 = 1;
const UCHAR DSC_ZTYPE_FIXED = 2;
const UCHAR DSC_ZTYPE_INT = 3;
const UCHAR DSC_ZTYPE_OTHER = 4;
const UCHAR DSC_ZTYPE_BAD = 5;
const UCHAR decimalDescTable[5][5] = {
/* DSC_ZTYPE_FLT64 DSC_ZTYPE_FLT128 DSC_ZTYPE_FIXED DSC_ZTYPE_INT DSC_ZTYPE_OTHER */
/* DSC_ZTYPE_FLT64 */ {DSC_ZTYPE_FLT64, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128},
/* DSC_ZTYPE_FLT128 */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128},
/* DSC_ZTYPE_FIXED */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_FIXED, DSC_ZTYPE_FLT128},
/* DSC_ZTYPE_INT */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD},
/* DSC_ZTYPE_OTHER */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD}
};
UCHAR getFType(const dsc& desc)
{
switch (desc.dsc_dtype)
{
case dtype_dec64:
return DSC_ZTYPE_FLT64;
case dtype_dec128:
return DSC_ZTYPE_FLT128;
case dtype_dec_fixed:
return DSC_ZTYPE_FIXED;
}
if (DTYPE_IS_EXACT(desc.dsc_dtype))
return DSC_ZTYPE_INT;
return DSC_ZTYPE_OTHER;
}
enum Scaling { SCALE_MIN, SCALE_SUM };
unsigned setDecDesc(dsc* desc, const dsc& desc1, const dsc& desc2, Scaling sc, SCHAR* nodScale = nullptr)
{
UCHAR zipType = decimalDescTable[getFType(desc1)][getFType(desc2)];
fb_assert(zipType <= DSC_ZTYPE_FIXED);
if (zipType > DSC_ZTYPE_FIXED)
zipType = DSC_ZTYPE_FLT128; // In production case fallback to Decimal128
desc->dsc_dtype = zipType == DSC_ZTYPE_FLT64 ? dtype_dec64 :
zipType == DSC_ZTYPE_FLT128 ? dtype_dec128 : dtype_dec_fixed;
desc->dsc_sub_type = 0;
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
desc->dsc_scale = 0;
if (zipType == DSC_ZTYPE_FIXED)
{
switch (sc)
{
case SCALE_MIN:
desc->dsc_scale = MIN(NUMERIC_SCALE(desc1), NUMERIC_SCALE(desc2));
break;
case SCALE_SUM:
desc->dsc_scale = NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2);
break;
}
}
if (nodScale)
*nodScale = desc->dsc_scale;
desc->dsc_length = zipType == DSC_ZTYPE_FLT64 ? sizeof(Decimal64) :
zipType == DSC_ZTYPE_FLT128 ? sizeof(Decimal128) : sizeof(DecimalFixed);
return zipType == DSC_ZTYPE_FIXED ? ExprNode::FLAG_DECFIXED : ExprNode::FLAG_DECFLOAT;
}
} // anon namespace
void ArithmeticNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
{
dsc desc1, desc2;
@ -650,10 +726,8 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2)
case dtype_dec64:
case dtype_dec128:
desc->dsc_dtype = dtype_dec128;
desc->dsc_sub_type = 0;
desc->dsc_scale = 0;
desc->dsc_length = sizeof(Decimal128);
case dtype_dec_fixed:
setDecDesc(desc, desc1, desc2, SCALE_MIN);
break;
default:
@ -680,10 +754,8 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2)
switch (dtype)
{
case dtype_dec128:
desc->dsc_dtype = dtype_dec128;
desc->dsc_sub_type = 0;
desc->dsc_scale = 0;
desc->dsc_length = sizeof(Decimal128);
case dtype_dec_fixed:
setDecDesc(desc, desc1, desc2, SCALE_SUM);
break;
case dtype_double:
@ -727,10 +799,7 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2)
if (DTYPE_IS_DECFLOAT(dtype))
{
desc->dsc_dtype = dtype_dec128;
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
setDecDesc(desc, desc1, desc2, SCALE_SUM);
break;
}
@ -779,7 +848,12 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2)
// the operation, as <timestamp>-<timestamp> uses
// <timestamp> arithmetic, but returns a <double>
if (DTYPE_IS_EXACT(dtype1) && DTYPE_IS_EXACT(dtype2))
{
if (desc1.isDecFixed() || desc2.isDecFixed())
dtype = dtype_dec_fixed;
else
dtype = dtype_int64;
}
else if (desc1.isDecOrInt() && desc2.isDecOrInt())
dtype = dtype_dec128;
else if (DTYPE_IS_NUMERIC(dtype1) && DTYPE_IS_NUMERIC(dtype2))
@ -904,10 +978,8 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2)
case dtype_dec64:
case dtype_dec128:
desc->dsc_dtype = dtype_dec128;
desc->dsc_sub_type = 0;
desc->dsc_scale = 0;
desc->dsc_length = sizeof(Decimal128);
case dtype_dec_fixed:
setDecDesc(desc, desc1, desc2, SCALE_MIN);
break;
case dtype_short:
@ -955,11 +1027,9 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2)
switch (dtype)
{
case dtype_dec_fixed:
case dtype_dec128:
desc->dsc_dtype = dtype_dec128;
desc->dsc_sub_type = 0;
desc->dsc_scale = 0;
desc->dsc_length = sizeof(Decimal128);
setDecDesc(desc, desc1, desc2, SCALE_SUM);
break;
case dtype_double:
@ -1016,8 +1086,8 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2)
break;
case dtype_dec128:
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;
case dtype_dec_fixed:
setDecDesc(desc, desc1, desc2, SCALE_SUM);
break;
default:
@ -1217,12 +1287,8 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1,
case dtype_dec64:
case dtype_dec128:
nodFlags |= FLAG_DECFLOAT;
desc->dsc_dtype = dtype_dec128;
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
case dtype_dec_fixed:
nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_MIN, &nodScale);
break;
case dtype_unknown:
@ -1268,12 +1334,8 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1,
return;
case dtype_dec128:
nodFlags |= FLAG_DECFLOAT;
desc->dsc_dtype = dtype_dec128;
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
case dtype_dec_fixed:
nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_SUM, &nodScale);
break;
case dtype_unknown:
@ -1357,8 +1419,13 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1,
// the result dtype. The rule is that two exact numeric operands yield an int64
// result, while an approximate numeric and anything yield a double result.
if (DTYPE_IS_EXACT(desc1.dsc_dtype) && DTYPE_IS_EXACT(desc2.dsc_dtype))
if (DTYPE_IS_EXACT(dtype1) && DTYPE_IS_EXACT(dtype2))
{
if (desc1.isDecFixed() || desc2.isDecFixed())
dtype = dtype_dec_fixed;
else
dtype = dtype_int64;
}
else if (desc1.isDecOrInt() && desc2.isDecOrInt())
dtype = dtype_dec128;
else if (DTYPE_IS_NUMERIC(desc1.dsc_dtype) && DTYPE_IS_NUMERIC(desc2.dsc_dtype))
@ -1493,12 +1560,8 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1,
case dtype_dec64:
case dtype_dec128:
nodFlags |= FLAG_DECFLOAT;
desc->dsc_dtype = dtype_dec128;
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
case dtype_dec_fixed:
nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_MIN, &nodScale);
return;
case dtype_short:
@ -1551,12 +1614,8 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1,
return;
case dtype_dec128:
nodFlags |= FLAG_DECFLOAT;
desc->dsc_dtype = dtype_dec128;
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;
desc->dsc_sub_type = 0;
desc->dsc_flags = 0;
case dtype_dec_fixed:
nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_SUM, &nodScale);
return;
case dtype_int64:
@ -1781,6 +1840,23 @@ dsc* ArithmeticNode::add(const dsc* desc, impure_value* value, const ValueExprNo
return result;
}
if (node->nodFlags & FLAG_DECFIXED)
{
const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, node->nodScale);
const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, node->nodScale);
DecimalStatus decSt = tdbb->getAttachment()->att_dec_status;
value->vlu_misc.vlu_dec_fixed = (blrOp == blr_subtract) ? d2.sub(decSt, d1) : d1.add(decSt, d2);
result->dsc_dtype = dtype_dec_fixed;
result->dsc_length = sizeof(DecimalFixed);
result->dsc_scale = node->nodScale;
result->dsc_sub_type = 0;
result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed;
return result;
}
// Handle floating arithmetic
if (node->nodFlags & FLAG_DOUBLE)
@ -1861,6 +1937,23 @@ dsc* ArithmeticNode::add2(const dsc* desc, impure_value* value, const ValueExprN
return result;
}
if (node->nodFlags & FLAG_DECFIXED)
{
const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, node->nodScale);
const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, node->nodScale);
DecimalStatus decSt = tdbb->getAttachment()->att_dec_status;
value->vlu_misc.vlu_dec_fixed = (blrOp == blr_subtract) ? d2.sub(decSt, d1) : d1.add(decSt, d2);
result->dsc_dtype = dtype_dec_fixed;
result->dsc_length = sizeof(DecimalFixed);
result->dsc_scale = node->nodScale;
result->dsc_sub_type = 0;
result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed;
return result;
}
// Handle floating arithmetic
if (node->nodFlags & FLAG_DOUBLE)
@ -1947,6 +2040,23 @@ dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const
return &value->vlu_desc;
}
if (nodFlags & FLAG_DECFIXED)
{
const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, nodScale);
const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, nodScale);
DecimalStatus decSt = tdbb->getAttachment()->att_dec_status;
value->vlu_misc.vlu_dec_fixed = d1.mul(decSt, d2);
value->vlu_desc.dsc_dtype = dtype_dec_fixed;
value->vlu_desc.dsc_length = sizeof(DecimalFixed);
value->vlu_desc.dsc_scale = nodScale;
value->vlu_desc.dsc_sub_type = 0;
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed;
return &value->vlu_desc;
}
// Handle floating arithmetic
if (nodFlags & FLAG_DOUBLE)
@ -2041,6 +2151,24 @@ dsc* ArithmeticNode::multiply2(const dsc* desc, impure_value* value) const
return &value->vlu_desc;
}
if (nodFlags & FLAG_DECFIXED)
{
const SSHORT scale = NUMERIC_SCALE(*desc);
const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, scale);
const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, nodScale - scale);
DecimalStatus decSt = tdbb->getAttachment()->att_dec_status;
value->vlu_misc.vlu_dec_fixed = d1.mul(decSt, d2);
value->vlu_desc.dsc_dtype = dtype_dec_fixed;
value->vlu_desc.dsc_length = sizeof(DecimalFixed);
value->vlu_desc.dsc_scale = nodScale;
value->vlu_desc.dsc_sub_type = 0;
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed;
return &value->vlu_desc;
}
// Handle floating arithmetic
if (nodFlags & FLAG_DOUBLE)
@ -2137,6 +2265,24 @@ dsc* ArithmeticNode::divide2(const dsc* desc, impure_value* value) const
return &value->vlu_desc;
}
if (nodFlags & FLAG_DECFIXED)
{
const SSHORT scale = NUMERIC_SCALE(*desc);
const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, desc, scale);
const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, nodScale - scale);
DecimalStatus decSt = tdbb->getAttachment()->att_dec_status;
value->vlu_misc.vlu_dec_fixed = d1.div(decSt, d2, scale * 2);
value->vlu_desc.dsc_dtype = dtype_dec_fixed;
value->vlu_desc.dsc_length = sizeof(DecimalFixed);
value->vlu_desc.dsc_scale = nodScale;
value->vlu_desc.dsc_sub_type = 0;
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed;
return &value->vlu_desc;
}
// Handle floating arithmetic
if (nodFlags & FLAG_DOUBLE)
@ -7829,6 +7975,10 @@ dsc* NegateNode::execute(thread_db* tdbb, jrd_req* request) const
impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.neg();
break;
case dtype_dec_fixed:
impure->vlu_misc.vlu_dec_fixed = impure->vlu_misc.vlu_dec_fixed.neg();
break;
case dtype_int64:
if (impure->vlu_misc.vlu_int64 == MIN_SINT64)
ERR_post(Arg::Gds(isc_exception_integer_overflow));
@ -10158,6 +10308,7 @@ void SubQueryNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
desc->dsc_dtype = dtype_dec128;
desc->dsc_length = sizeof(Decimal128);
desc->dsc_scale = 0;

View File

@ -526,6 +526,7 @@ public:
static const unsigned FLAG_DATE = 0x20;
static const unsigned FLAG_DECFLOAT = 0x40;
static const unsigned FLAG_VALUE = 0x80; // Full value area required in impure space.
static const unsigned FLAG_DECFIXED = 0x100;
explicit ExprNode(Type aType, MemoryPool& pool, Kind aKind)
: DmlNode(pool, aKind),

View File

@ -59,7 +59,8 @@ const USHORT blr_dtypes[] = {
0, // DB_KEY
blr_bool, // dtype_boolean
blr_dec64, // dtype_dec64
blr_dec128 // dtype_dec128
blr_dec128, // dtype_dec128
blr_dec_fixed // dtype_dec_fixed
};
bool DDL_ids(const Jrd::DsqlCompilerScratch*);

View File

@ -257,6 +257,10 @@ public:
precision = 18;
break;
case dtype_dec_fixed:
precision = 34;
break;
default:
fb_assert(!DTYPE_IS_EXACT(dtype));
}

View File

@ -393,6 +393,11 @@ void GEN_descriptor( DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool tex
dsqlScratch->appendUChar(blr_dec128);
break;
case dtype_dec_fixed:
dsqlScratch->appendUChar(blr_dec_fixed);
dsqlScratch->appendUChar(desc->dsc_scale);
break;
case dtype_sql_date:
dsqlScratch->appendUChar(blr_sql_date);
break;

View File

@ -4842,10 +4842,15 @@ prec_scale
{
$$ = newNode<dsql_fld>();
if ($2 < 1 || $2 > 18)
yyabandon(YYPOSNARG(2), -842, isc_precision_err); // Precision must be between 1 and 18.
if ($2 < 1 || $2 > 34)
yyabandon(YYPOSNARG(2), -842, isc_precision_err/*2!!!!!*/); // Precision must be between 1 and 34.
if ($2 > 9)
if ($2 > 18)
{
$$->dtype = dtype_dec_fixed;
$$->length = sizeof(DecimalFixed);
}
else if ($2 > 9)
{
if ( ( (client_dialect <= SQL_DIALECT_V5) && (db_dialect > SQL_DIALECT_V5) ) ||
( (client_dialect > SQL_DIALECT_V5) && (db_dialect <= SQL_DIALECT_V5) ) )
@ -4892,13 +4897,18 @@ prec_scale
{
$$ = newNode<dsql_fld>();
if ($2 < 1 || $2 > 18)
yyabandon(YYPOSNARG(2), -842, isc_precision_err); // Precision should be between 1 and 18
if ($2 < 1 || $2 > 34)
yyabandon(YYPOSNARG(2), -842, isc_precision_err/*2!!!!!*/); // Precision must be between 1 and 34.
if ($4 > $2 || $4 < 0)
yyabandon(YYPOSNARG(4), -842, isc_scale_nogt); // Scale must be between 0 and precision
if ($2 > 9)
if ($2 > 18)
{
$$->dtype = dtype_dec_fixed;
$$->length = sizeof(DecimalFixed);
}
else if ($2 > 9)
{
if ( ( (client_dialect <= SQL_DIALECT_V5) && (db_dialect > SQL_DIALECT_V5) ) ||
( (client_dialect > SQL_DIALECT_V5) && (db_dialect <= SQL_DIALECT_V5) ) )

View File

@ -78,6 +78,7 @@ typedef struct
#define SQL_TYPE_TIME 560
#define SQL_TYPE_DATE 570
#define SQL_INT64 580
#define SQL_DEC_FIXED 32758
#define SQL_DEC16 32760
#define SQL_DEC34 32762
#define SQL_BOOLEAN 32764

View File

@ -31,6 +31,7 @@ typedef ISC_QUAD;
typedef ISC_TIME;
typedef FB_DEC16;
typedef FB_DEC34;
typedef FB_DEC_FIXED;
// Versioned interface - base for all FB interfaces
interface Versioned
@ -989,6 +990,7 @@ version: // 3.0 => 4.0
EventBlock createEventBlock(Status status, const string* events);
DecFloat16 getDecFloat16(Status status);
DecFloat34 getDecFloat34(Status status);
DecFixed getDecFixed(Status status);
}
interface OffsetsCallback : Versioned
@ -1392,3 +1394,13 @@ interface DecFloat34 : Versioned
void fromBcd(int sign, const uchar* bcd, int exp, FB_DEC34* to);
void fromString(Status status, const string from, FB_DEC34* to);
}
interface DecFixed : Versioned
{
const uint BCD_SIZE = 34;
const uint STRING_SIZE = 41; // may include exponent not more than 3 digits
void toBcd(const FB_DEC_FIXED* from, int* sign, uchar* bcd);
void toString(Status status, const FB_DEC_FIXED* from, int scale, uint bufferLength, string buffer);
void fromBcd(int sign, const uchar* bcd, FB_DEC_FIXED* to);
void fromString(Status status, const string from, int scale, FB_DEC_FIXED* to);
}

View File

@ -113,6 +113,7 @@ namespace Firebird
class IUdrPlugin;
class IDecFloat16;
class IDecFloat34;
class IDecFixed;
// Interfaces declarations
@ -3699,6 +3700,7 @@ namespace Firebird
IEventBlock* (CLOOP_CARG *createEventBlock)(IUtil* self, IStatus* status, const char** events) throw();
IDecFloat16* (CLOOP_CARG *getDecFloat16)(IUtil* self, IStatus* status) throw();
IDecFloat34* (CLOOP_CARG *getDecFloat34)(IUtil* self, IStatus* status) throw();
IDecFixed* (CLOOP_CARG *getDecFixed)(IUtil* self, IStatus* status) throw();
};
protected:
@ -3841,6 +3843,20 @@ namespace Firebird
StatusType::checkException(status);
return ret;
}
template <typename StatusType> IDecFixed* getDecFixed(StatusType* status)
{
if (cloopVTable->version < 3)
{
StatusType::setVersionError(status, "IUtil", cloopVTable->version, 3);
StatusType::checkException(status);
return 0;
}
StatusType::clearException(status);
IDecFixed* ret = static_cast<VTable*>(this->cloopVTable)->getDecFixed(this, status);
StatusType::checkException(status);
return ret;
}
};
class IOffsetsCallback : public IVersioned
@ -5420,6 +5436,58 @@ namespace Firebird
}
};
class IDecFixed : public IVersioned
{
public:
struct VTable : public IVersioned::VTable
{
void (CLOOP_CARG *toBcd)(IDecFixed* self, const FB_DEC_FIXED* from, int* sign, unsigned char* bcd) throw();
void (CLOOP_CARG *toString)(IDecFixed* self, IStatus* status, const FB_DEC_FIXED* from, int scale, unsigned bufferLength, char* buffer) throw();
void (CLOOP_CARG *fromBcd)(IDecFixed* self, int sign, const unsigned char* bcd, FB_DEC_FIXED* to) throw();
void (CLOOP_CARG *fromString)(IDecFixed* self, IStatus* status, const char* from, int scale, FB_DEC_FIXED* to) throw();
};
protected:
IDecFixed(DoNotInherit)
: IVersioned(DoNotInherit())
{
}
~IDecFixed()
{
}
public:
static const unsigned VERSION = 2;
static const unsigned BCD_SIZE = 34;
static const unsigned STRING_SIZE = 41;
void toBcd(const FB_DEC_FIXED* from, int* sign, unsigned char* bcd)
{
static_cast<VTable*>(this->cloopVTable)->toBcd(this, from, sign, bcd);
}
template <typename StatusType> void toString(StatusType* status, const FB_DEC_FIXED* from, int scale, unsigned bufferLength, char* buffer)
{
StatusType::clearException(status);
static_cast<VTable*>(this->cloopVTable)->toString(this, status, from, scale, bufferLength, buffer);
StatusType::checkException(status);
}
void fromBcd(int sign, const unsigned char* bcd, FB_DEC_FIXED* to)
{
static_cast<VTable*>(this->cloopVTable)->fromBcd(this, sign, bcd, to);
}
template <typename StatusType> void fromString(StatusType* status, const char* from, int scale, FB_DEC_FIXED* to)
{
StatusType::clearException(status);
static_cast<VTable*>(this->cloopVTable)->fromString(this, status, from, scale, to);
StatusType::checkException(status);
}
};
// Interfaces implementations
template <typename Name, typename StatusType, typename Base>
@ -13093,6 +13161,7 @@ namespace Firebird
this->createEventBlock = &Name::cloopcreateEventBlockDispatcher;
this->getDecFloat16 = &Name::cloopgetDecFloat16Dispatcher;
this->getDecFloat34 = &Name::cloopgetDecFloat34Dispatcher;
this->getDecFixed = &Name::cloopgetDecFixedDispatcher;
}
} vTable;
@ -13320,6 +13389,21 @@ namespace Firebird
return static_cast<IDecFloat34*>(0);
}
}
static IDecFixed* CLOOP_CARG cloopgetDecFixedDispatcher(IUtil* self, IStatus* status) throw()
{
StatusType status2(status);
try
{
return static_cast<Name*>(self)->Name::getDecFixed(&status2);
}
catch (...)
{
StatusType::catchException(&status2);
return static_cast<IDecFixed*>(0);
}
}
};
template <typename Name, typename StatusType, typename Base = IVersionedImpl<Name, StatusType, Inherit<IUtil> > >
@ -13351,6 +13435,7 @@ namespace Firebird
virtual IEventBlock* createEventBlock(StatusType* status, const char** events) = 0;
virtual IDecFloat16* getDecFloat16(StatusType* status) = 0;
virtual IDecFloat34* getDecFloat34(StatusType* status) = 0;
virtual IDecFixed* getDecFixed(StatusType* status) = 0;
};
template <typename Name, typename StatusType, typename Base>
@ -16748,6 +16833,101 @@ namespace Firebird
virtual void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC34* to) = 0;
virtual void fromString(StatusType* status, const char* from, FB_DEC34* to) = 0;
};
template <typename Name, typename StatusType, typename Base>
class IDecFixedBaseImpl : public Base
{
public:
typedef IDecFixed Declaration;
IDecFixedBaseImpl(DoNotInherit = DoNotInherit())
{
static struct VTableImpl : Base::VTable
{
VTableImpl()
{
this->version = Base::VERSION;
this->toBcd = &Name::clooptoBcdDispatcher;
this->toString = &Name::clooptoStringDispatcher;
this->fromBcd = &Name::cloopfromBcdDispatcher;
this->fromString = &Name::cloopfromStringDispatcher;
}
} vTable;
this->cloopVTable = &vTable;
}
static void CLOOP_CARG clooptoBcdDispatcher(IDecFixed* self, const FB_DEC_FIXED* from, int* sign, unsigned char* bcd) throw()
{
try
{
static_cast<Name*>(self)->Name::toBcd(from, sign, bcd);
}
catch (...)
{
StatusType::catchException(0);
}
}
static void CLOOP_CARG clooptoStringDispatcher(IDecFixed* self, IStatus* status, const FB_DEC_FIXED* from, int scale, unsigned bufferLength, char* buffer) throw()
{
StatusType status2(status);
try
{
static_cast<Name*>(self)->Name::toString(&status2, from, scale, bufferLength, buffer);
}
catch (...)
{
StatusType::catchException(&status2);
}
}
static void CLOOP_CARG cloopfromBcdDispatcher(IDecFixed* self, int sign, const unsigned char* bcd, FB_DEC_FIXED* to) throw()
{
try
{
static_cast<Name*>(self)->Name::fromBcd(sign, bcd, to);
}
catch (...)
{
StatusType::catchException(0);
}
}
static void CLOOP_CARG cloopfromStringDispatcher(IDecFixed* self, IStatus* status, const char* from, int scale, FB_DEC_FIXED* to) throw()
{
StatusType status2(status);
try
{
static_cast<Name*>(self)->Name::fromString(&status2, from, scale, to);
}
catch (...)
{
StatusType::catchException(&status2);
}
}
};
template <typename Name, typename StatusType, typename Base = IVersionedImpl<Name, StatusType, Inherit<IDecFixed> > >
class IDecFixedImpl : public IDecFixedBaseImpl<Name, StatusType, Base>
{
protected:
IDecFixedImpl(DoNotInherit = DoNotInherit())
{
}
public:
virtual ~IDecFixedImpl()
{
}
virtual void toBcd(const FB_DEC_FIXED* from, int* sign, unsigned char* bcd) = 0;
virtual void toString(StatusType* status, const FB_DEC_FIXED* from, int scale, unsigned bufferLength, char* buffer) = 0;
virtual void fromBcd(int sign, const unsigned char* bcd, FB_DEC_FIXED* to) = 0;
virtual void fromString(StatusType* status, const char* from, int scale, FB_DEC_FIXED* to) = 0;
};
};

View File

@ -141,6 +141,11 @@
builder->setType(status, index, SQL_DEC34); \
builder->setLength(status, index, sizeof(FB_DEC34));
#define FB__META_FB_DEC_FIXED(scale) \
builder->setType(status, index, SQL_DEC_FIXED); \
builder->setLength(status, index, sizeof(FB_DEC_FIXED)); \
builder->setScale(status, index, scale);
#define FB__META_FB_BLOB \
builder->setType(status, index, SQL_BLOB); \
builder->setLength(status, index, sizeof(ISC_QUAD));
@ -195,6 +200,7 @@
#define FB__TYPE_FB_DOUBLE double
#define FB__TYPE_FB_DECFLOAT16 FB_DEC16
#define FB__TYPE_FB_DECFLOAT34 FB_DEC34
#define FB__TYPE_FB_DEC_FIXED FB_DEC_FIXED
#define FB__TYPE_FB_BLOB ISC_QUAD
#define FB__TYPE_FB_BOOLEAN ISC_UCHAR
#define FB__TYPE_FB_DATE ::Firebird::FbDate

View File

@ -178,7 +178,12 @@ struct FB_DEC34_t {
ISC_UINT64 fb_data[2];
};
struct FB_DEC_FIXED_t {
ISC_UINT64 fb_data[2];
};
typedef struct FB_DEC16_t FB_DEC16;
typedef struct FB_DEC34_t FB_DEC34;
typedef struct FB_DEC_FIXED_t FB_DEC_FIXED;
#endif /* INCLUDE_TYPES_PUB_H */

View File

@ -317,6 +317,7 @@ IsqlGlobals::IsqlGlobals()
df16 = Firebird::UtilInterfacePtr()->getDecFloat16(&statusWrapper);
df34 = Firebird::UtilInterfacePtr()->getDecFloat34(&statusWrapper);
dfix = Firebird::UtilInterfacePtr()->getDecFixed(&statusWrapper);
}
// I s q l G l o b a l s : : p r i n t f
@ -1835,6 +1836,7 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int
case INTEGER:
case BIGINT:
case DOUBLE_PRECISION:
case DEC_FIXED_TYPE:
// Handle Integral subtypes NUMERIC and DECIMAL
// We are ODS >= 10 and could be any Dialect
@ -1868,6 +1870,9 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int
case DOUBLE_PRECISION:
isqlGlob.printf("NUMERIC(15, %d)", -fieldScale);
break;
case DEC_FIXED_TYPE:
isqlGlob.printf("NUMERIC(34, %d)", -fieldScale);
break;
}
}
else
@ -1981,6 +1986,50 @@ void ISQL_print_validation(FILE* fp,
}
static Firebird::string get_numeric_value(const char* fromStr)
{
/**************************************
*
* g e t _ n u m e r i c _ v a l u e
*
**************************************
*
* Functional description
* Prepares data for converting into decfloat by interface function.
*
**************************************/
Firebird::string val;
for (const char* q = fromStr; *q; ++q)
{
switch (*q)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case '+':
case '-':
case 'e':
case 'E':
val += *q;
break;
default:
return val;
}
}
return val;
}
static processing_state add_row(TEXT* tabname)
{
/**************************************
@ -2237,6 +2286,7 @@ static processing_state add_row(TEXT* tabname)
double* dvalue;
FB_DEC16* d64value;
FB_DEC34* d128value;
FB_DEC_FIXED* dfixvalue;
UCHAR* boolean;
ISC_QUAD* blobid;
vary* avary;
@ -2325,6 +2375,23 @@ static processing_state add_row(TEXT* tabname)
}
break;
case SQL_DEC_FIXED:
scale = msg->getScale(fbStatus, i);
if (ISQL_errmsg(fbStatus))
return (SKIP);
dfixvalue = (FB_DEC_FIXED*) datap;
if (isqlGlob.dfix)
isqlGlob.dfix->fromString(fbStatus, lastInputLine, scale, dfixvalue);
if ((!isqlGlob.dfix) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS))
{
STDERROUT("Input parsing problem");
done = true;
}
break;
case SQL_TYPE_DATE:
if (3 != sscanf(lastInputLine, "%d/%d/%d", &times.tm_year,
&times.tm_mon, &times.tm_mday) ||
@ -3016,6 +3083,7 @@ static processing_state bulk_insert_hack(const char* command)
tm times;
FB_DEC16* d64value;
FB_DEC34* d128value;
FB_DEC_FIXED* dfixvalue;
// Initialize the time structure.
memset(&times, 0, sizeof(times));
char msec_str[5] = "";
@ -3078,7 +3146,7 @@ static processing_state bulk_insert_hack(const char* command)
case SQL_DEC16:
d64value = (FB_DEC16*) datap;
if (isqlGlob.df16)
isqlGlob.df16->fromString(fbStatus, lastInputLine, d64value);
isqlGlob.df16->fromString(fbStatus, get_numeric_value(lastPos).c_str(), d64value);
if ((!isqlGlob.df16) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS))
{
STDERROUT("Input parsing problem");
@ -3089,7 +3157,7 @@ static processing_state bulk_insert_hack(const char* command)
case SQL_DEC34:
d128value = (FB_DEC34*) datap;
if (isqlGlob.df34)
isqlGlob.df34->fromString(fbStatus, lastInputLine, d128value);
isqlGlob.df34->fromString(fbStatus, get_numeric_value(lastPos).c_str(), d128value);
if ((!isqlGlob.df34) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS))
{
STDERROUT("Input parsing problem");
@ -3097,6 +3165,23 @@ static processing_state bulk_insert_hack(const char* command)
}
break;
case SQL_DEC_FIXED:
scale = message->getScale(fbStatus, i);
if (ISQL_errmsg(fbStatus))
return (SKIP);
dfixvalue = (FB_DEC_FIXED*) datap;
if (isqlGlob.dfix)
{
isqlGlob.dfix->fromString(fbStatus, get_numeric_value(lastPos).c_str(), scale, dfixvalue);
}
if ((!isqlGlob.dfix) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS))
{
STDERROUT("Input parsing problem");
done = true;
}
break;
case SQL_TYPE_DATE:
if (3 != sscanf(lastPos, "%d-%d-%d", &times.tm_year,
&times.tm_mon, &times.tm_mday) ||
@ -7272,6 +7357,25 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length)
}
break;
case SQL_DEC_FIXED:
{
char decStr[Firebird::IDecFixed::STRING_SIZE];
if (isqlGlob.dfix)
{
isqlGlob.dfix->toString(fbStatus, var->value.asDecFixed, dscale, sizeof(decStr), decStr);
if (ISQL_errmsg(fbStatus))
strcpy(decStr, convErr);
}
else
strcpy(decStr, convErr);
if (setValues.List)
isqlGlob.printf("%*.*s%s", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr, NEWLINE);
else
sprintf(p, "%*.*s ", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr);
}
break;
case SQL_TEXT:
str2 = var->value.asChar;
// See if it is character set OCTETS
@ -8054,6 +8158,9 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns
case SQL_DEC34:
disp_length = Firebird::IDecFloat34::STRING_SIZE - 1;
break;
case SQL_DEC_FIXED:
disp_length = Firebird::IDecFixed::STRING_SIZE - 1;
break;
case SQL_TEXT:
alignment = 1;
data_length++;
@ -8710,6 +8817,8 @@ static const char* sqltype_to_string(unsigned sqltype)
return "DECFLOAT(16)";
case SQL_DEC34:
return "DECFLOAT(34)";
case SQL_DEC_FIXED:
return "DECIMAL FIXED";
case SQL_D_FLOAT:
return "D_FLOAT";
case SQL_TIMESTAMP:

View File

@ -297,6 +297,7 @@ const int BIGINT = 16;
const int BOOLEAN_TYPE = 23;
const int DEC64_TYPE = 24;
const int DEC128_TYPE = 25;
const int DEC_FIXED_TYPE = 26;
static const sqltypes Column_types[] = {
{SMALLINT, "SMALLINT"}, // keyword
@ -316,6 +317,7 @@ static const sqltypes Column_types[] = {
{BOOLEAN_TYPE, "BOOLEAN"}, // keyword
{DEC64_TYPE, "DECFLOAT(16)"},
{DEC128_TYPE, "DECFLOAT(34)"},
{DEC_FIXED_TYPE, "<Should not be shown>"},
{0, ""}
};
@ -404,6 +406,7 @@ public:
USHORT att_charset;
Firebird::IDecFloat16* df16;
Firebird::IDecFloat34* df34;
Firebird::IDecFixed* dfix;
void printf(const char* buffer, ...);
void prints(const char* buffer);
@ -463,6 +466,7 @@ struct IsqlVar
char* asChar;
FB_DEC16* asDec16;
FB_DEC34* asDec34;
FB_DEC_FIXED* asDecFixed;
void* setPtr;
};
TypeMix value;

View File

@ -109,6 +109,12 @@ namespace
item.length = sizeof(Decimal128);
break;
case dtype_dec_fixed:
item.type = SQL_DEC_FIXED;
item.scale = desc->dsc_scale;
item.length = sizeof(DecimalFixed);
break;
case dtype_sql_date:
item.type = SQL_TYPE_DATE;
item.length = sizeof(SLONG);

View File

@ -70,7 +70,7 @@ static const USHORT gds_cvt_blr_dtype[DTYPE_BLR_MAX + 1] =
dtype_boolean, // blr_bool == 23
dtype_dec64, /* blr_dec64 == 24 */
dtype_dec128, /* blr_dec128 == 25 */
0,
dtype_dec_fixed, /* blr_dec_fixed == 26 */
dtype_double, /* blr_double == 27 */
0, 0, 0, 0, 0, 0, 0,
dtype_timestamp, /* blr_timestamp == 35 */
@ -108,7 +108,8 @@ static const USHORT type_alignments[DTYPE_TYPE_MAX] =
sizeof(ULONG), /* dtype_dbkey */
sizeof(UCHAR), /* dtype_boolean */
sizeof(Firebird::Decimal64),/* dtype_dec64 */
sizeof(Firebird::Decimal64) /* dtype_dec128 */
sizeof(Firebird::Decimal64),/* dtype_dec128 */
sizeof(Firebird::Decimal64) /* dtype_dec_fixed */
};
static const USHORT type_lengths[DTYPE_TYPE_MAX] =
@ -136,7 +137,8 @@ static const USHORT type_lengths[DTYPE_TYPE_MAX] =
sizeof(RecordNumber::Packed),/*dtype_dbkey */
sizeof(UCHAR), /* dtype_boolean */
sizeof(Firebird::Decimal64),/* dtype_dec64 */
sizeof(Firebird::Decimal128)/* dtype_dec128 */
sizeof(Firebird::Decimal128),/*dtype_dec128 */
sizeof(Firebird::DecimalFixed) /* dtype_dec_fixed */
};
@ -167,7 +169,8 @@ static const USHORT type_significant_bits[DTYPE_TYPE_MAX] =
0, // dtype_dbkey
0, // dtype_boolean
0, // dtype_dec64
0 // dtype_dec128
0, // dtype_dec128
0 // dtype_dec_fixed
};
#endif /* JRD_ALIGN_H */

View File

@ -69,6 +69,7 @@
#define blr_bool (unsigned char)23
#define blr_dec64 (unsigned char)24
#define blr_dec128 (unsigned char)25
#define blr_dec_fixed (unsigned char)26
// first sub parameter for blr_domain_name[2]
#define blr_domain_type_of (unsigned char)0

View File

@ -292,6 +292,9 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_dec128:
return ((Decimal128*) p1)->compare(decSt, *(Decimal128*) p2);
case dtype_dec_fixed:
return ((DecimalFixed*) p1)->compare(decSt, *(DecimalFixed*) p2);
case dtype_boolean:
return *p1 == *p2 ? 0 : *p1 < *p2 ? -1 : 1;
@ -524,6 +527,19 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
return temp1.compare(decSt, temp2);
}
case dtype_dec_fixed:
{
SSHORT scale;
if (arg2->dsc_dtype > dtype_varying)
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
else
scale = arg1->dsc_scale;
const DecimalFixed temp1 = CVT_get_dec_fixed(arg1, scale, decSt, ERR_post);
const DecimalFixed temp2 = CVT_get_dec_fixed(arg2, scale, decSt, ERR_post);
return temp1.compare(decSt, temp2);
}
case dtype_blob:
return CVT2_blob_compare(arg1, arg2, decSt);

View File

@ -1243,6 +1243,7 @@ USHORT DFW_assign_index_type(thread_db* tdbb, const Firebird::MetaName& name, SS
return idx_boolean;
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
return idx_decimal;
default:
return idx_numeric;

View File

@ -434,6 +434,10 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor
value->vlu_misc.vlu_dec128 = *((Decimal128*) from.dsc_address);
return;
case dtype_dec_fixed:
value->vlu_misc.vlu_dec_fixed = *((DecimalFixed*) from.dsc_address);
return;
case dtype_timestamp:
case dtype_quad:
value->vlu_misc.vlu_dbkey[0] = ((SLONG*) from.dsc_address)[0];

View File

@ -605,6 +605,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
break;
case dtype_dec128:
case dtype_dec_fixed:
{
const Decimal128 d = MOV_get_dec128(tdbb, input);
if (parameter->prm_fun_mechanism == FUN_value)
@ -818,6 +819,21 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
Arg::Str(function->getName().toString()));
}
break;
case dtype_dec_fixed:
if (value->vlu_misc.vlu_dec_fixed.isInf())
{
status_exception::raise(Arg::Gds(isc_expression_eval_err) <<
Arg::Gds(isc_udf_fp_overflow) <<
Arg::Str(function->getName().toString()));
}
else if (value->vlu_misc.vlu_dec_fixed.isNan())
{
status_exception::raise(Arg::Gds(isc_expression_eval_err) <<
Arg::Gds(isc_udf_fp_nan) <<
Arg::Str(function->getName().toString()));
}
break;
}
request->req_flags &= ~req_null;
@ -1120,6 +1136,10 @@ static void invoke(thread_db* tdbb,
value->vlu_misc.vlu_dec128 = CALL_UDF<Decimal128>(tdbb, function->fun_entrypoint, args);
break;
case dtype_dec_fixed:
value->vlu_misc.vlu_dec_fixed = CALL_UDF<DecimalFixed>(tdbb, function->fun_entrypoint, args);
break;
case dtype_timestamp:
default:
udfError = UeUnsupDtype;

View File

@ -440,6 +440,18 @@ Decimal128 MOV_get_dec128(Jrd::thread_db* tdbb, const dsc* desc)
}
DecimalFixed MOV_get_dec_fixed(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale)
{
/**************************************
*
* M O V _ g e t _ d e c _ f i x e d
*
**************************************/
return CVT_get_dec_fixed(desc, scale, tdbb->getAttachment()->att_dec_status, ERR_post);
}
namespace Jrd
{

View File

@ -52,6 +52,7 @@ Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT
void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*);
Firebird::Decimal64 MOV_get_dec64(Jrd::thread_db*, const dsc*);
Firebird::Decimal128 MOV_get_dec128(Jrd::thread_db*, const dsc*);
Firebird::DecimalFixed MOV_get_dec_fixed(Jrd::thread_db*, const dsc*, SSHORT);
namespace Jrd
{

View File

@ -396,7 +396,8 @@ static const UCHAR sort_dtypes[] =
SKD_text, // dtype_dbkey - use text sort for backward compatibility
SKD_bytes, // dtype_boolean
SKD_dec64, // dtype_dec64
SKD_dec128 // dtype_dec128
SKD_dec128, // dtype_dec128
SKD_dec_fixed // dtype_dec_fixed
};

View File

@ -395,6 +395,12 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
desc->dsc_length = sizeof(Decimal128);
break;
case blr_dec_fixed:
desc->dsc_dtype = dtype_dec_fixed;
desc->dsc_length = sizeof(DecimalFixed);
desc->dsc_scale = (int) blrReader.getByte();
break;
case blr_blob2:
desc->dsc_dtype = dtype_blob;
desc->dsc_length = sizeof(ISC_QUAD);

View File

@ -143,6 +143,7 @@ const int SKD_sql_date = 14;
const int SKD_int64 = 15;
const int SKD_dec64 = 16;
const int SKD_dec128 = 17;
const int SKD_dec_fixed = 18;
// skd_flags
const UCHAR SKD_ascending = 0; // default initializer

View File

@ -84,6 +84,7 @@ struct impure_value
double vlu_double;
Firebird::Decimal64 vlu_dec64;
Firebird::Decimal128 vlu_dec128;
Firebird::DecimalFixed vlu_dec_fixed;
GDS_TIMESTAMP vlu_timestamp;
GDS_TIME vlu_sql_time;
GDS_DATE vlu_sql_date;
@ -97,6 +98,7 @@ struct impure_value
void make_int64(const SINT64 val, const signed char scale = 0);
void make_double(const double val);
void make_decimal128(const Firebird::Decimal128 val);
void make_decimal_fixed(const Firebird::DecimalFixed val, const signed char scale);
};
// Do not use these methods where dsc_sub_type is not explicitly set to zero.
@ -140,6 +142,16 @@ inline void impure_value::make_decimal128(const Firebird::Decimal128 val)
this->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&this->vlu_misc.vlu_dec128);
}
inline void impure_value::make_decimal_fixed(const Firebird::DecimalFixed val, const signed char scale)
{
this->vlu_misc.vlu_dec_fixed = val;
this->vlu_desc.dsc_dtype = dtype_dec_fixed;
this->vlu_desc.dsc_length = sizeof(Firebird::DecimalFixed);
this->vlu_desc.dsc_scale = scale;
this->vlu_desc.dsc_sub_type = 0;
this->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&this->vlu_misc.vlu_dec_fixed);
}
struct impure_value_ex : public impure_value
{
SINT64 vlux_count;

View File

@ -17,6 +17,7 @@
ISC_QUAD = array [1..2] of Integer;
FB_DEC16 = array [1..1] of Int64;
FB_DEC34 = array [1..2] of Int64;
FB_DEC_FIXED = array [1..2] of Int64;
ntrace_relation_t = Integer;
TraceCounts = Record

View File

@ -127,6 +127,12 @@ void BlrFromMessage::buildBlr(IMessageMetadata* metadata)
dtype = dtype_dec128;
break;
case SQL_DEC_FIXED:
appendUChar(blr_dec_fixed);
appendUChar(scale);
dtype = dtype_dec_fixed;
break;
case SQL_DOUBLE:
appendUChar(blr_double);
dtype = dtype_double;

View File

@ -300,6 +300,13 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length)
align = type_alignments[dtype_dec128];
break;
case blr_dec_fixed:
desc->dsc_dtype = dtype_dec_fixed;
desc->dsc_length = sizeof(DecimalFixed);
desc->dsc_scale = *blr++;
align = type_alignments[dtype_dec_fixed];
break;
// this case cannot occur as switch paramater is char and blr_blob
// is 261. blob_ids are actually passed around as blr_quad.

View File

@ -755,6 +755,9 @@ void TracePluginImpl::appendParams(ITraceParams* params)
case dtype_dec128:
paramtype = "decfloat(34)";
break;
case dtype_dec_fixed:
paramtype = "decimal";
break;
case dtype_sql_date:
paramtype = "date";
@ -877,6 +880,20 @@ void TracePluginImpl::appendParams(ITraceParams* params)
((Decimal128*) parameters->dsc_address)->toString(paramvalue);
break;
case dtype_dec_fixed:
try
{
DecimalStatus decSt(DEC_Errors);
((DecimalFixed*) parameters->dsc_address)->toString(decSt, parameters->dsc_scale, paramvalue);
}
catch (const Exception& ex)
{
StaticStatusVector status;
ex.stuffException(status);
paramvalue.printf("Conversion error %d\n", status[1]);
}
break;
case dtype_sql_date:
{
struct tm times;

View File

@ -590,6 +590,7 @@ public:
Firebird::IEventBlock* createEventBlock(Firebird::CheckStatusWrapper* status, const char** events);
Firebird::IDecFloat16* getDecFloat16(Firebird::CheckStatusWrapper* status);
Firebird::IDecFloat34* getDecFloat34(Firebird::CheckStatusWrapper* status);
Firebird::IDecFixed* getDecFixed(Firebird::CheckStatusWrapper* status);
};
} // namespace Why

View File

@ -1076,7 +1076,7 @@ public:
{
char temp[STRING_SIZE];
decDoubleToString(reinterpret_cast<const decDouble*>(from), temp);
int len = strlen(temp);
unsigned int len = strlen(temp);
if (len < bufSize)
strncpy(buffer, temp, bufSize);
else
@ -1137,7 +1137,7 @@ public:
{
char temp[STRING_SIZE];
decQuadToString(reinterpret_cast<const decQuad*>(from), temp);
int len = strlen(temp);
unsigned int len = strlen(temp);
if (len < bufSize)
strncpy(buffer, temp, bufSize);
else
@ -1179,6 +1179,56 @@ IDecFloat34* UtilInterface::getDecFloat34(CheckStatusWrapper* status)
return &decFloat34;
}
class DecFixed FB_FINAL : public AutoIface<IDecFixedImpl<DecFixed, CheckStatusWrapper> >
{
public:
// IDecFixed implementation
void toBcd(const FB_DEC_FIXED* from, int* sign, unsigned char* bcd)
{
int exp = 0;
*sign = decQuadToBCD(reinterpret_cast<const decQuad*>(from), &exp, bcd);
fb_assert(exp == 0);
}
void toString(CheckStatusWrapper* status, const FB_DEC_FIXED* from, int scale, unsigned bufSize, char* buffer)
{
try
{
DecimalStatus decSt(DEC_Errors);
reinterpret_cast<const DecimalFixed*>(from)->toString(decSt, scale, bufSize, buffer);
}
catch (const Exception& ex)
{
ex.stuffException(status);
}
}
void fromBcd(int sign, const unsigned char* bcd, FB_DEC_FIXED* to)
{
decQuadFromBCD(reinterpret_cast<decQuad*>(to), 0, bcd, sign ? DECFLOAT_Sign : 0);
}
void fromString(CheckStatusWrapper* status, const char* from, int scale, FB_DEC_FIXED* to)
{
try
{
DecimalStatus decSt(DEC_Errors);
DecimalFixed* val = reinterpret_cast<DecimalFixed*>(to);
val->set(from, scale, decSt);
}
catch (const Exception& ex)
{
ex.stuffException(status);
}
}
};
IDecFixed* UtilInterface::getDecFixed(CheckStatusWrapper* /*status*/)
{
static DecFixed decFixed;
return &decFixed;
}
unsigned UtilInterface::setOffsets(CheckStatusWrapper* status, IMessageMetadata* metadata,
IOffsetsCallback* callback)
{