in driver/utility.cc [2803:2951]
void sqlnum_from_str(const char *numstr, SQL_NUMERIC_STRUCT *sqlnum,
int *overflow_ptr)
{
/*
We use 16 bits of each integer to convert the
current segment of the number leaving extra bits
to multiply/carry
*/
unsigned build_up[8], tmp_prec_calc[8];
/* current segment as integer */
unsigned int curnum;
/* current segment digits copied for strtoul() */
char curdigs[5];
/* number of digits in current segment */
int usedig;
int i;
int len;
char *decpt= strchr((char*)numstr, '.');
int overflow= 0;
SQLSCHAR reqscale= sqlnum->scale;
SQLCHAR reqprec= sqlnum->precision;
memset(&sqlnum->val, 0, sizeof(sqlnum->val));
memset(build_up, 0, sizeof(build_up));
/* handle sign */
if (!(sqlnum->sign= !(*numstr == '-')))
++numstr;
len= (int) strlen(numstr);
sqlnum->precision= len;
sqlnum->scale= 0;
/* process digits in groups of <=4 */
for (i= 0; i < len; i += usedig)
{
if (i + 4 < len)
usedig= 4;
else
usedig= len - i;
/*
if we have the decimal point, ignore it by setting it to the
last char (will be ignored by strtoul)
*/
if (decpt && decpt >= numstr + i && decpt < numstr + i + usedig)
{
usedig = (int) (decpt - (numstr + i) + 1);
sqlnum->scale= len - (i + usedig);
--sqlnum->precision;
decpt= NULL;
}
if (overflow)
goto end;
/* grab just this piece, and convert to int */
memcpy(curdigs, numstr + i, usedig);
curdigs[usedig]= 0;
curnum= strtoul(curdigs, NULL, 10);
if (curdigs[usedig - 1] == '.')
sqlnum_scale(build_up, usedig - 1);
else
sqlnum_scale(build_up, usedig);
/* add the current number */
build_up[0] += curnum;
sqlnum_carry(build_up);
if (build_up[7] & ~0xffff)
overflow= 1;
}
/* scale up to SQL_DESC_SCALE */
if (reqscale > 0 && reqscale > sqlnum->scale)
{
while (reqscale > sqlnum->scale)
{
sqlnum_scale(build_up, 1);
sqlnum_carry(build_up);
++sqlnum->scale;
}
}
/* scale back, truncating decimals */
else if (reqscale < sqlnum->scale)
{
while (reqscale < sqlnum->scale && sqlnum->scale > 0)
{
sqlnum_unscale_le(build_up);
// Value 2 of overflow indicates truncation, not critical
if (build_up[0] % 10)
overflow = 2;
build_up[0] /= 10;
--sqlnum->precision;
--sqlnum->scale;
}
}
/* scale back whole numbers while there's no significant digits */
if (reqscale < 0)
{
memcpy(tmp_prec_calc, build_up, sizeof(build_up));
while (reqscale < sqlnum->scale)
{
sqlnum_unscale_le(tmp_prec_calc);
if (tmp_prec_calc[0] % 10)
{
overflow= 1;
goto end;
}
sqlnum_unscale_le(build_up);
tmp_prec_calc[0] /= 10;
build_up[0] /= 10;
--sqlnum->precision;
--sqlnum->scale;
}
}
/* calculate minimum precision */
memcpy(tmp_prec_calc, build_up, sizeof(build_up));
{
SQLCHAR temp_precision = sqlnum->precision;
do
{
sqlnum_unscale_le(tmp_prec_calc);
i= tmp_prec_calc[0] % 10;
tmp_prec_calc[0] /= 10;
if (i == 0)
--temp_precision;
} while (i == 0 && temp_precision > 0);
/* detect precision overflow */
if (temp_precision > reqprec)
overflow= 1;
}
/* compress results into SQL_NUMERIC_STRUCT.val */
for (i= 0; i < 8; ++i)
{
int elem= 2 * i;
sqlnum->val[elem]= build_up[i] & 0xff;
sqlnum->val[elem+1]= (build_up[i] >> 8) & 0xff;
}
end:
if (overflow_ptr)
*overflow_ptr= overflow;
}