LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - numeric.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 86.2 % 725 625
Test Date: 2026-03-03 14:15:12 Functions: 96.6 % 29 28
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* src/interfaces/ecpg/pgtypeslib/numeric.c */
       2              : 
       3              : #include "postgres_fe.h"
       4              : 
       5              : #include <ctype.h>
       6              : #include <float.h>
       7              : #include <limits.h>
       8              : 
       9              : #include "pgtypes_error.h"
      10              : #include "pgtypes_numeric.h"
      11              : #include "pgtypeslib_extern.h"
      12              : 
      13              : #define Max(x, y)               ((x) > (y) ? (x) : (y))
      14              : #define Min(x, y)               ((x) < (y) ? (x) : (y))
      15              : 
      16              : #define init_var(v)             memset(v,0,sizeof(numeric))
      17              : 
      18              : #define digitbuf_alloc(size) ((NumericDigit *) pgtypes_alloc(size))
      19              : #define digitbuf_free(buf) free(buf)
      20              : 
      21              : 
      22              : /* ----------
      23              :  *  alloc_var() -
      24              :  *
      25              :  *   Allocate a digit buffer of ndigits digits (plus a spare digit for rounding)
      26              :  * ----------
      27              :  */
      28              : static int
      29        27356 : alloc_var(numeric *var, int ndigits)
      30              : {
      31        27356 :     digitbuf_free(var->buf);
      32        27356 :     var->buf = digitbuf_alloc(ndigits + 1);
      33        27356 :     if (var->buf == NULL)
      34            0 :         return -1;
      35        27356 :     var->buf[0] = 0;
      36        27356 :     var->digits = var->buf + 1;
      37        27356 :     var->ndigits = ndigits;
      38        27356 :     return 0;
      39              : }
      40              : 
      41              : numeric *
      42        15266 : PGTYPESnumeric_new(void)
      43              : {
      44              :     numeric    *var;
      45              : 
      46        15266 :     if ((var = (numeric *) pgtypes_alloc(sizeof(numeric))) == NULL)
      47            0 :         return NULL;
      48              : 
      49        15266 :     if (alloc_var(var, 0) < 0)
      50              :     {
      51            0 :         free(var);
      52            0 :         return NULL;
      53              :     }
      54              : 
      55        15266 :     return var;
      56              : }
      57              : 
      58              : decimal *
      59           92 : PGTYPESdecimal_new(void)
      60              : {
      61              :     decimal    *var;
      62              : 
      63           92 :     if ((var = (decimal *) pgtypes_alloc(sizeof(decimal))) == NULL)
      64            0 :         return NULL;
      65              : 
      66           92 :     memset(var, 0, sizeof(decimal));
      67              : 
      68           92 :     return var;
      69              : }
      70              : 
      71              : /* ----------
      72              :  * set_var_from_str()
      73              :  *
      74              :  *  Parse a string and put the number into a variable
      75              :  * ----------
      76              :  */
      77              : static int
      78          170 : set_var_from_str(char *str, char **ptr, numeric *dest)
      79              : {
      80          170 :     bool        have_dp = false;
      81          170 :     int         i = 0;
      82              : 
      83          170 :     errno = 0;
      84          170 :     *ptr = str;
      85          170 :     while (*(*ptr))
      86              :     {
      87          170 :         if (!isspace((unsigned char) *(*ptr)))
      88          170 :             break;
      89            0 :         (*ptr)++;
      90              :     }
      91              : 
      92          170 :     if (pg_strncasecmp(*ptr, "NaN", 3) == 0)
      93              :     {
      94           24 :         *ptr += 3;
      95           24 :         dest->sign = NUMERIC_NAN;
      96              : 
      97              :         /* Should be nothing left but spaces */
      98           24 :         while (*(*ptr))
      99              :         {
     100            0 :             if (!isspace((unsigned char) *(*ptr)))
     101              :             {
     102            0 :                 errno = PGTYPES_NUM_BAD_NUMERIC;
     103            0 :                 return -1;
     104              :             }
     105            0 :             (*ptr)++;
     106              :         }
     107              : 
     108           24 :         return 0;
     109              :     }
     110              : 
     111          146 :     if (alloc_var(dest, strlen((*ptr))) < 0)
     112            0 :         return -1;
     113          146 :     dest->weight = -1;
     114          146 :     dest->dscale = 0;
     115          146 :     dest->sign = NUMERIC_POS;
     116              : 
     117          146 :     switch (*(*ptr))
     118              :     {
     119            8 :         case '+':
     120            8 :             dest->sign = NUMERIC_POS;
     121            8 :             (*ptr)++;
     122            8 :             break;
     123              : 
     124           18 :         case '-':
     125           18 :             dest->sign = NUMERIC_NEG;
     126           18 :             (*ptr)++;
     127           18 :             break;
     128              :     }
     129              : 
     130          146 :     if (*(*ptr) == '.')
     131              :     {
     132           20 :         have_dp = true;
     133           20 :         (*ptr)++;
     134              :     }
     135              : 
     136          146 :     if (!isdigit((unsigned char) *(*ptr)))
     137              :     {
     138            4 :         errno = PGTYPES_NUM_BAD_NUMERIC;
     139            4 :         return -1;
     140              :     }
     141              : 
     142          918 :     while (*(*ptr))
     143              :     {
     144          806 :         if (isdigit((unsigned char) *(*ptr)))
     145              :         {
     146          684 :             dest->digits[i++] = *(*ptr)++ - '0';
     147          684 :             if (!have_dp)
     148          436 :                 dest->weight++;
     149              :             else
     150          248 :                 dest->dscale++;
     151              :         }
     152          122 :         else if (*(*ptr) == '.')
     153              :         {
     154           92 :             if (have_dp)
     155              :             {
     156            0 :                 errno = PGTYPES_NUM_BAD_NUMERIC;
     157            0 :                 return -1;
     158              :             }
     159           92 :             have_dp = true;
     160           92 :             (*ptr)++;
     161              :         }
     162              :         else
     163           30 :             break;
     164              :     }
     165          142 :     dest->ndigits = i;
     166              : 
     167              :     /* Handle exponent, if any */
     168          142 :     if (*(*ptr) == 'e' || *(*ptr) == 'E')
     169              :     {
     170              :         long        exponent;
     171              :         char       *endptr;
     172              : 
     173           30 :         (*ptr)++;
     174           30 :         exponent = strtol(*ptr, &endptr, 10);
     175           30 :         if (endptr == (*ptr))
     176              :         {
     177            0 :             errno = PGTYPES_NUM_BAD_NUMERIC;
     178            0 :             return -1;
     179              :         }
     180           30 :         (*ptr) = endptr;
     181           30 :         if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
     182              :         {
     183            0 :             errno = PGTYPES_NUM_BAD_NUMERIC;
     184            0 :             return -1;
     185              :         }
     186           30 :         dest->weight += (int) exponent;
     187           30 :         dest->dscale -= (int) exponent;
     188           30 :         if (dest->dscale < 0)
     189           14 :             dest->dscale = 0;
     190              :     }
     191              : 
     192              :     /* Should be nothing left but spaces */
     193          142 :     while (*(*ptr))
     194              :     {
     195            0 :         if (!isspace((unsigned char) *(*ptr)))
     196              :         {
     197            0 :             errno = PGTYPES_NUM_BAD_NUMERIC;
     198            0 :             return -1;
     199              :         }
     200            0 :         (*ptr)++;
     201              :     }
     202              : 
     203              :     /* Strip any leading zeroes */
     204          192 :     while (dest->ndigits > 0 && *(dest->digits) == 0)
     205              :     {
     206           50 :         (dest->digits)++;
     207           50 :         (dest->weight)--;
     208           50 :         (dest->ndigits)--;
     209              :     }
     210          142 :     if (dest->ndigits == 0)
     211           16 :         dest->weight = 0;
     212              : 
     213          142 :     dest->rscale = dest->dscale;
     214          142 :     return 0;
     215              : }
     216              : 
     217              : 
     218              : /* ----------
     219              :  * get_str_from_var() -
     220              :  *
     221              :  *  Convert a var to text representation (guts of numeric_out).
     222              :  *  CAUTION: var's contents may be modified by rounding!
     223              :  * ----------
     224              :  */
     225              : static char *
     226         4006 : get_str_from_var(numeric *var, int dscale)
     227              : {
     228              :     char       *str;
     229              :     char       *cp;
     230              :     int         i;
     231              :     int         d;
     232              : 
     233         4006 :     if (var->sign == NUMERIC_NAN)
     234              :     {
     235           10 :         str = pgtypes_alloc(4);
     236           10 :         if (str == NULL)
     237            0 :             return NULL;
     238           10 :         sprintf(str, "NaN");
     239           10 :         return str;
     240              :     }
     241              : 
     242              :     /*
     243              :      * Check if we must round up before printing the value and do so.
     244              :      */
     245         3996 :     i = dscale + var->weight + 1;
     246         3996 :     if (i >= 0 && var->ndigits > i)
     247          576 :     {
     248          576 :         int         carry = (var->digits[i] > 4) ? 1 : 0;
     249              : 
     250          576 :         var->ndigits = i;
     251              : 
     252         2918 :         while (carry)
     253              :         {
     254         2342 :             carry += var->digits[--i];
     255         2342 :             var->digits[i] = carry % 10;
     256         2342 :             carry /= 10;
     257              :         }
     258              : 
     259          576 :         if (i < 0)
     260              :         {
     261           40 :             var->digits--;
     262           40 :             var->ndigits++;
     263           40 :             var->weight++;
     264              :         }
     265              :     }
     266              :     else
     267         3420 :         var->ndigits = Max(0, Min(i, var->ndigits));
     268              : 
     269              :     /*
     270              :      * Allocate space for the result
     271              :      */
     272         3996 :     if ((str = pgtypes_alloc(Max(0, dscale) + Max(0, var->weight) + 4)) == NULL)
     273            0 :         return NULL;
     274         3996 :     cp = str;
     275              : 
     276              :     /*
     277              :      * Output a dash for negative values
     278              :      */
     279         3996 :     if (var->sign == NUMERIC_NEG)
     280         1276 :         *cp++ = '-';
     281              : 
     282              :     /*
     283              :      * Output all digits before the decimal point
     284              :      */
     285         3996 :     i = Max(var->weight, 0);
     286         3996 :     d = 0;
     287              : 
     288       164486 :     while (i >= 0)
     289              :     {
     290       160490 :         if (i <= var->weight && d < var->ndigits)
     291        70958 :             *cp++ = var->digits[d++] + '0';
     292              :         else
     293        89532 :             *cp++ = '0';
     294       160490 :         i--;
     295              :     }
     296              : 
     297              :     /*
     298              :      * If requested, output a decimal point and all the digits that follow it.
     299              :      */
     300         3996 :     if (dscale > 0)
     301              :     {
     302         3654 :         *cp++ = '.';
     303        91496 :         while (i >= -dscale)
     304              :         {
     305        87842 :             if (i <= var->weight && d < var->ndigits)
     306        14420 :                 *cp++ = var->digits[d++] + '0';
     307              :             else
     308        73422 :                 *cp++ = '0';
     309        87842 :             i--;
     310              :         }
     311              :     }
     312              : 
     313              :     /*
     314              :      * terminate the string and return it
     315              :      */
     316         3996 :     *cp = '\0';
     317         3996 :     return str;
     318              : }
     319              : 
     320              : numeric *
     321          170 : PGTYPESnumeric_from_asc(char *str, char **endptr)
     322              : {
     323          170 :     numeric    *value = (numeric *) pgtypes_alloc(sizeof(numeric));
     324              :     int         ret;
     325              : 
     326              :     char       *realptr;
     327          170 :     char      **ptr = (endptr != NULL) ? endptr : &realptr;
     328              : 
     329          170 :     if (!value)
     330            0 :         return NULL;
     331              : 
     332          170 :     ret = set_var_from_str(str, ptr, value);
     333          170 :     if (ret)
     334              :     {
     335            4 :         PGTYPESnumeric_free(value);
     336            4 :         return NULL;
     337              :     }
     338              : 
     339          166 :     return value;
     340              : }
     341              : 
     342              : char *
     343         3950 : PGTYPESnumeric_to_asc(numeric *num, int dscale)
     344              : {
     345         3950 :     numeric    *numcopy = PGTYPESnumeric_new();
     346              :     char       *s;
     347              : 
     348         3950 :     if (numcopy == NULL)
     349            0 :         return NULL;
     350              : 
     351         3950 :     if (PGTYPESnumeric_copy(num, numcopy) < 0)
     352              :     {
     353            0 :         PGTYPESnumeric_free(numcopy);
     354            0 :         return NULL;
     355              :     }
     356              : 
     357         3950 :     if (dscale < 0)
     358          112 :         dscale = num->dscale;
     359              : 
     360              :     /* get_str_from_var may change its argument */
     361         3950 :     s = get_str_from_var(numcopy, dscale);
     362         3950 :     PGTYPESnumeric_free(numcopy);
     363         3950 :     return s;
     364              : }
     365              : 
     366              : /* ----------
     367              :  * zero_var() -
     368              :  *
     369              :  *  Set a variable to ZERO.
     370              :  *  Note: rscale and dscale are not touched.
     371              :  * ----------
     372              :  */
     373              : static void
     374        10140 : zero_var(numeric *var)
     375              : {
     376        10140 :     digitbuf_free(var->buf);
     377        10140 :     var->buf = NULL;
     378        10140 :     var->digits = NULL;
     379        10140 :     var->ndigits = 0;
     380        10140 :     var->weight = 0;         /* by convention; doesn't really matter */
     381        10140 :     var->sign = NUMERIC_POS; /* anything but NAN... */
     382        10140 : }
     383              : 
     384              : void
     385        15396 : PGTYPESnumeric_free(numeric *var)
     386              : {
     387        15396 :     digitbuf_free(var->buf);
     388        15396 :     free(var);
     389        15396 : }
     390              : 
     391              : void
     392           92 : PGTYPESdecimal_free(decimal *var)
     393              : {
     394           92 :     free(var);
     395           92 : }
     396              : 
     397              : /* ----------
     398              :  * cmp_abs() -
     399              :  *
     400              :  *  Compare the absolute values of var1 and var2
     401              :  *  Returns:    -1 for ABS(var1) < ABS(var2)
     402              :  *              0  for ABS(var1) == ABS(var2)
     403              :  *              1  for ABS(var1) > ABS(var2)
     404              :  * ----------
     405              :  */
     406              : static int
     407        47524 : cmp_abs(numeric *var1, numeric *var2)
     408              : {
     409        47524 :     int         i1 = 0;
     410        47524 :     int         i2 = 0;
     411        47524 :     int         w1 = var1->weight;
     412        47524 :     int         w2 = var2->weight;
     413              :     int         stat;
     414              : 
     415        47524 :     while (w1 > w2 && i1 < var1->ndigits)
     416              :     {
     417          430 :         if (var1->digits[i1++] != 0)
     418          430 :             return 1;
     419            0 :         w1--;
     420              :     }
     421        59922 :     while (w2 > w1 && i2 < var2->ndigits)
     422              :     {
     423        16196 :         if (var2->digits[i2++] != 0)
     424         3368 :             return -1;
     425        12828 :         w2--;
     426              :     }
     427              : 
     428        43726 :     if (w1 == w2)
     429              :     {
     430        78324 :         while (i1 < var1->ndigits && i2 < var2->ndigits)
     431              :         {
     432        76656 :             stat = var1->digits[i1++] - var2->digits[i2++];
     433        76656 :             if (stat)
     434              :             {
     435        41958 :                 if (stat > 0)
     436        22596 :                     return 1;
     437        19362 :                 return -1;
     438              :             }
     439              :         }
     440              :     }
     441              : 
     442         1932 :     while (i1 < var1->ndigits)
     443              :     {
     444          850 :         if (var1->digits[i1++] != 0)
     445          686 :             return 1;
     446              :     }
     447         1526 :     while (i2 < var2->ndigits)
     448              :     {
     449         1122 :         if (var2->digits[i2++] != 0)
     450          678 :             return -1;
     451              :     }
     452              : 
     453          404 :     return 0;
     454              : }
     455              : 
     456              : 
     457              : /* ----------
     458              :  * add_abs() -
     459              :  *
     460              :  *  Add the absolute values of two variables into result.
     461              :  *  result might point to one of the operands without danger.
     462              :  * ----------
     463              :  */
     464              : static int
     465          846 : add_abs(numeric *var1, numeric *var2, numeric *result)
     466              : {
     467              :     NumericDigit *res_buf;
     468              :     NumericDigit *res_digits;
     469              :     int         res_ndigits;
     470              :     int         res_weight;
     471              :     int         res_rscale;
     472              :     int         res_dscale;
     473              :     int         i,
     474              :                 i1,
     475              :                 i2;
     476          846 :     int         carry = 0;
     477              : 
     478              :     /* copy these values into local vars for speed in inner loop */
     479          846 :     int         var1ndigits = var1->ndigits;
     480          846 :     int         var2ndigits = var2->ndigits;
     481          846 :     NumericDigit *var1digits = var1->digits;
     482          846 :     NumericDigit *var2digits = var2->digits;
     483              : 
     484          846 :     res_weight = Max(var1->weight, var2->weight) + 1;
     485          846 :     res_rscale = Max(var1->rscale, var2->rscale);
     486          846 :     res_dscale = Max(var1->dscale, var2->dscale);
     487          846 :     res_ndigits = res_rscale + res_weight + 1;
     488          846 :     if (res_ndigits <= 0)
     489            0 :         res_ndigits = 1;
     490              : 
     491          846 :     if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)
     492            0 :         return -1;
     493          846 :     res_digits = res_buf;
     494              : 
     495          846 :     i1 = res_rscale + var1->weight + 1;
     496          846 :     i2 = res_rscale + var2->weight + 1;
     497        98930 :     for (i = res_ndigits - 1; i >= 0; i--)
     498              :     {
     499        98084 :         i1--;
     500        98084 :         i2--;
     501        98084 :         if (i1 >= 0 && i1 < var1ndigits)
     502         5062 :             carry += var1digits[i1];
     503        98084 :         if (i2 >= 0 && i2 < var2ndigits)
     504         5060 :             carry += var2digits[i2];
     505              : 
     506        98084 :         if (carry >= 10)
     507              :         {
     508          432 :             res_digits[i] = carry - 10;
     509          432 :             carry = 1;
     510              :         }
     511              :         else
     512              :         {
     513        97652 :             res_digits[i] = carry;
     514        97652 :             carry = 0;
     515              :         }
     516              :     }
     517              : 
     518         4920 :     while (res_ndigits > 0 && *res_digits == 0)
     519              :     {
     520         4074 :         res_digits++;
     521         4074 :         res_weight--;
     522         4074 :         res_ndigits--;
     523              :     }
     524         6044 :     while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
     525         5198 :         res_ndigits--;
     526              : 
     527          846 :     if (res_ndigits == 0)
     528            4 :         res_weight = 0;
     529              : 
     530          846 :     digitbuf_free(result->buf);
     531          846 :     result->ndigits = res_ndigits;
     532          846 :     result->buf = res_buf;
     533          846 :     result->digits = res_digits;
     534          846 :     result->weight = res_weight;
     535          846 :     result->rscale = res_rscale;
     536          846 :     result->dscale = res_dscale;
     537              : 
     538          846 :     return 0;
     539              : }
     540              : 
     541              : 
     542              : /* ----------
     543              :  * sub_abs() -
     544              :  *
     545              :  *  Subtract the absolute value of var2 from the absolute value of var1
     546              :  *  and store in result. result might point to one of the operands
     547              :  *  without danger.
     548              :  *
     549              :  *  ABS(var1) MUST BE GREATER OR EQUAL ABS(var2) !!!
     550              :  * ----------
     551              :  */
     552              : static int
     553        23962 : sub_abs(numeric *var1, numeric *var2, numeric *result)
     554              : {
     555              :     NumericDigit *res_buf;
     556              :     NumericDigit *res_digits;
     557              :     int         res_ndigits;
     558              :     int         res_weight;
     559              :     int         res_rscale;
     560              :     int         res_dscale;
     561              :     int         i,
     562              :                 i1,
     563              :                 i2;
     564        23962 :     int         borrow = 0;
     565              : 
     566              :     /* copy these values into local vars for speed in inner loop */
     567        23962 :     int         var1ndigits = var1->ndigits;
     568        23962 :     int         var2ndigits = var2->ndigits;
     569        23962 :     NumericDigit *var1digits = var1->digits;
     570        23962 :     NumericDigit *var2digits = var2->digits;
     571              : 
     572        23962 :     res_weight = var1->weight;
     573        23962 :     res_rscale = Max(var1->rscale, var2->rscale);
     574        23962 :     res_dscale = Max(var1->dscale, var2->dscale);
     575        23962 :     res_ndigits = res_rscale + res_weight + 1;
     576        23962 :     if (res_ndigits <= 0)
     577            0 :         res_ndigits = 1;
     578              : 
     579        23962 :     if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)
     580            0 :         return -1;
     581        23962 :     res_digits = res_buf;
     582              : 
     583        23962 :     i1 = res_rscale + var1->weight + 1;
     584        23962 :     i2 = res_rscale + var2->weight + 1;
     585       363990 :     for (i = res_ndigits - 1; i >= 0; i--)
     586              :     {
     587       340028 :         i1--;
     588       340028 :         i2--;
     589       340028 :         if (i1 >= 0 && i1 < var1ndigits)
     590       204164 :             borrow += var1digits[i1];
     591       340028 :         if (i2 >= 0 && i2 < var2ndigits)
     592       203248 :             borrow -= var2digits[i2];
     593              : 
     594       340028 :         if (borrow < 0)
     595              :         {
     596       165678 :             res_digits[i] = borrow + 10;
     597       165678 :             borrow = -1;
     598              :         }
     599              :         else
     600              :         {
     601       174350 :             res_digits[i] = borrow;
     602       174350 :             borrow = 0;
     603              :         }
     604              :     }
     605              : 
     606        50692 :     while (res_ndigits > 0 && *res_digits == 0)
     607              :     {
     608        26730 :         res_digits++;
     609        26730 :         res_weight--;
     610        26730 :         res_ndigits--;
     611              :     }
     612        51858 :     while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
     613        27896 :         res_ndigits--;
     614              : 
     615        23962 :     if (res_ndigits == 0)
     616            0 :         res_weight = 0;
     617              : 
     618        23962 :     digitbuf_free(result->buf);
     619        23962 :     result->ndigits = res_ndigits;
     620        23962 :     result->buf = res_buf;
     621        23962 :     result->digits = res_digits;
     622        23962 :     result->weight = res_weight;
     623        23962 :     result->rscale = res_rscale;
     624        23962 :     result->dscale = res_dscale;
     625              : 
     626        23962 :     return 0;
     627              : }
     628              : 
     629              : /* ----------
     630              :  * add_var() -
     631              :  *
     632              :  *  Full version of add functionality on variable level (handling signs).
     633              :  *  result might point to one of the operands too without danger.
     634              :  * ----------
     635              :  */
     636              : int
     637          846 : PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result)
     638              : {
     639              :     /*
     640              :      * Decide on the signs of the two variables what to do
     641              :      */
     642          846 :     if (var1->sign == NUMERIC_POS)
     643              :     {
     644          614 :         if (var2->sign == NUMERIC_POS)
     645              :         {
     646              :             /*
     647              :              * Both are positive result = +(ABS(var1) + ABS(var2))
     648              :              */
     649          446 :             if (add_abs(var1, var2, result) != 0)
     650            0 :                 return -1;
     651          446 :             result->sign = NUMERIC_POS;
     652              :         }
     653              :         else
     654              :         {
     655              :             /*
     656              :              * var1 is positive, var2 is negative Must compare absolute values
     657              :              */
     658          168 :             switch (cmp_abs(var1, var2))
     659              :             {
     660            0 :                 case 0:
     661              :                     /* ----------
     662              :                      * ABS(var1) == ABS(var2)
     663              :                      * result = ZERO
     664              :                      * ----------
     665              :                      */
     666            0 :                     zero_var(result);
     667            0 :                     result->rscale = Max(var1->rscale, var2->rscale);
     668            0 :                     result->dscale = Max(var1->dscale, var2->dscale);
     669            0 :                     break;
     670              : 
     671           92 :                 case 1:
     672              :                     /* ----------
     673              :                      * ABS(var1) > ABS(var2)
     674              :                      * result = +(ABS(var1) - ABS(var2))
     675              :                      * ----------
     676              :                      */
     677           92 :                     if (sub_abs(var1, var2, result) != 0)
     678            0 :                         return -1;
     679           92 :                     result->sign = NUMERIC_POS;
     680           92 :                     break;
     681              : 
     682           76 :                 case -1:
     683              :                     /* ----------
     684              :                      * ABS(var1) < ABS(var2)
     685              :                      * result = -(ABS(var2) - ABS(var1))
     686              :                      * ----------
     687              :                      */
     688           76 :                     if (sub_abs(var2, var1, result) != 0)
     689            0 :                         return -1;
     690           76 :                     result->sign = NUMERIC_NEG;
     691           76 :                     break;
     692              :             }
     693              :         }
     694              :     }
     695              :     else
     696              :     {
     697          232 :         if (var2->sign == NUMERIC_POS)
     698              :         {
     699              :             /* ----------
     700              :              * var1 is negative, var2 is positive
     701              :              * Must compare absolute values
     702              :              * ----------
     703              :              */
     704          168 :             switch (cmp_abs(var1, var2))
     705              :             {
     706            0 :                 case 0:
     707              :                     /* ----------
     708              :                      * ABS(var1) == ABS(var2)
     709              :                      * result = ZERO
     710              :                      * ----------
     711              :                      */
     712            0 :                     zero_var(result);
     713            0 :                     result->rscale = Max(var1->rscale, var2->rscale);
     714            0 :                     result->dscale = Max(var1->dscale, var2->dscale);
     715            0 :                     break;
     716              : 
     717           76 :                 case 1:
     718              :                     /* ----------
     719              :                      * ABS(var1) > ABS(var2)
     720              :                      * result = -(ABS(var1) - ABS(var2))
     721              :                      * ----------
     722              :                      */
     723           76 :                     if (sub_abs(var1, var2, result) != 0)
     724            0 :                         return -1;
     725           76 :                     result->sign = NUMERIC_NEG;
     726           76 :                     break;
     727              : 
     728           92 :                 case -1:
     729              :                     /* ----------
     730              :                      * ABS(var1) < ABS(var2)
     731              :                      * result = +(ABS(var2) - ABS(var1))
     732              :                      * ----------
     733              :                      */
     734           92 :                     if (sub_abs(var2, var1, result) != 0)
     735            0 :                         return -1;
     736           92 :                     result->sign = NUMERIC_POS;
     737           92 :                     break;
     738              :             }
     739              :         }
     740              :         else
     741              :         {
     742              :             /* ----------
     743              :              * Both are negative
     744              :              * result = -(ABS(var1) + ABS(var2))
     745              :              * ----------
     746              :              */
     747           64 :             if (add_abs(var1, var2, result) != 0)
     748            0 :                 return -1;
     749           64 :             result->sign = NUMERIC_NEG;
     750              :         }
     751              :     }
     752              : 
     753          846 :     return 0;
     754              : }
     755              : 
     756              : 
     757              : /* ----------
     758              :  * sub_var() -
     759              :  *
     760              :  *  Full version of sub functionality on variable level (handling signs).
     761              :  *  result might point to one of the operands too without danger.
     762              :  * ----------
     763              :  */
     764              : int
     765          844 : PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result)
     766              : {
     767              :     /*
     768              :      * Decide on the signs of the two variables what to do
     769              :      */
     770          844 :     if (var1->sign == NUMERIC_POS)
     771              :     {
     772          612 :         if (var2->sign == NUMERIC_NEG)
     773              :         {
     774              :             /* ----------
     775              :              * var1 is positive, var2 is negative
     776              :              * result = +(ABS(var1) + ABS(var2))
     777              :              * ----------
     778              :              */
     779          168 :             if (add_abs(var1, var2, result) != 0)
     780            0 :                 return -1;
     781          168 :             result->sign = NUMERIC_POS;
     782              :         }
     783              :         else
     784              :         {
     785              :             /* ----------
     786              :              * Both are positive
     787              :              * Must compare absolute values
     788              :              * ----------
     789              :              */
     790          444 :             switch (cmp_abs(var1, var2))
     791              :             {
     792           42 :                 case 0:
     793              :                     /* ----------
     794              :                      * ABS(var1) == ABS(var2)
     795              :                      * result = ZERO
     796              :                      * ----------
     797              :                      */
     798           42 :                     zero_var(result);
     799           42 :                     result->rscale = Max(var1->rscale, var2->rscale);
     800           42 :                     result->dscale = Max(var1->dscale, var2->dscale);
     801           42 :                     break;
     802              : 
     803          202 :                 case 1:
     804              :                     /* ----------
     805              :                      * ABS(var1) > ABS(var2)
     806              :                      * result = +(ABS(var1) - ABS(var2))
     807              :                      * ----------
     808              :                      */
     809          202 :                     if (sub_abs(var1, var2, result) != 0)
     810            0 :                         return -1;
     811          202 :                     result->sign = NUMERIC_POS;
     812          202 :                     break;
     813              : 
     814          200 :                 case -1:
     815              :                     /* ----------
     816              :                      * ABS(var1) < ABS(var2)
     817              :                      * result = -(ABS(var2) - ABS(var1))
     818              :                      * ----------
     819              :                      */
     820          200 :                     if (sub_abs(var2, var1, result) != 0)
     821            0 :                         return -1;
     822          200 :                     result->sign = NUMERIC_NEG;
     823          200 :                     break;
     824              :             }
     825              :         }
     826              :     }
     827              :     else
     828              :     {
     829          232 :         if (var2->sign == NUMERIC_NEG)
     830              :         {
     831              :             /* ----------
     832              :              * Both are negative
     833              :              * Must compare absolute values
     834              :              * ----------
     835              :              */
     836           64 :             switch (cmp_abs(var1, var2))
     837              :             {
     838           16 :                 case 0:
     839              :                     /* ----------
     840              :                      * ABS(var1) == ABS(var2)
     841              :                      * result = ZERO
     842              :                      * ----------
     843              :                      */
     844           16 :                     zero_var(result);
     845           16 :                     result->rscale = Max(var1->rscale, var2->rscale);
     846           16 :                     result->dscale = Max(var1->dscale, var2->dscale);
     847           16 :                     break;
     848              : 
     849           24 :                 case 1:
     850              :                     /* ----------
     851              :                      * ABS(var1) > ABS(var2)
     852              :                      * result = -(ABS(var1) - ABS(var2))
     853              :                      * ----------
     854              :                      */
     855           24 :                     if (sub_abs(var1, var2, result) != 0)
     856            0 :                         return -1;
     857           24 :                     result->sign = NUMERIC_NEG;
     858           24 :                     break;
     859              : 
     860           24 :                 case -1:
     861              :                     /* ----------
     862              :                      * ABS(var1) < ABS(var2)
     863              :                      * result = +(ABS(var2) - ABS(var1))
     864              :                      * ----------
     865              :                      */
     866           24 :                     if (sub_abs(var2, var1, result) != 0)
     867            0 :                         return -1;
     868           24 :                     result->sign = NUMERIC_POS;
     869           24 :                     break;
     870              :             }
     871              :         }
     872              :         else
     873              :         {
     874              :             /* ----------
     875              :              * var1 is negative, var2 is positive
     876              :              * result = -(ABS(var1) + ABS(var2))
     877              :              * ----------
     878              :              */
     879          168 :             if (add_abs(var1, var2, result) != 0)
     880            0 :                 return -1;
     881          168 :             result->sign = NUMERIC_NEG;
     882              :         }
     883              :     }
     884              : 
     885          844 :     return 0;
     886              : }
     887              : 
     888              : /* ----------
     889              :  * mul_var() -
     890              :  *
     891              :  *  Multiplication on variable level. Product of var1 * var2 is stored
     892              :  *  in result.  Accuracy of result is determined by global_rscale.
     893              :  * ----------
     894              :  */
     895              : int
     896          846 : PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result)
     897              : {
     898              :     NumericDigit *res_buf;
     899              :     NumericDigit *res_digits;
     900              :     int         res_ndigits;
     901              :     int         res_weight;
     902              :     int         res_sign;
     903              :     int         i,
     904              :                 ri,
     905              :                 i1,
     906              :                 i2;
     907          846 :     long        sum = 0;
     908          846 :     int         global_rscale = var1->rscale + var2->rscale;
     909              : 
     910          846 :     res_weight = var1->weight + var2->weight + 2;
     911          846 :     res_ndigits = var1->ndigits + var2->ndigits + 1;
     912          846 :     if (var1->sign == var2->sign)
     913          510 :         res_sign = NUMERIC_POS;
     914              :     else
     915          336 :         res_sign = NUMERIC_NEG;
     916              : 
     917          846 :     if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)
     918            0 :         return -1;
     919          846 :     res_digits = res_buf;
     920          846 :     memset(res_digits, 0, res_ndigits);
     921              : 
     922          846 :     ri = res_ndigits;
     923         5922 :     for (i1 = var1->ndigits - 1; i1 >= 0; i1--)
     924              :     {
     925         5076 :         sum = 0;
     926         5076 :         i = --ri;
     927              : 
     928        36214 :         for (i2 = var2->ndigits - 1; i2 >= 0; i2--)
     929              :         {
     930        31138 :             sum += res_digits[i] + var1->digits[i1] * var2->digits[i2];
     931        31138 :             res_digits[i--] = sum % 10;
     932        31138 :             sum /= 10;
     933              :         }
     934         5076 :         res_digits[i] = sum;
     935              :     }
     936              : 
     937          846 :     i = res_weight + global_rscale + 2;
     938          846 :     if (i >= 0 && i < res_ndigits)
     939              :     {
     940            0 :         sum = (res_digits[i] > 4) ? 1 : 0;
     941            0 :         res_ndigits = i;
     942            0 :         i--;
     943            0 :         while (sum)
     944              :         {
     945            0 :             sum += res_digits[i];
     946            0 :             res_digits[i--] = sum % 10;
     947            0 :             sum /= 10;
     948              :         }
     949              :     }
     950              : 
     951         2740 :     while (res_ndigits > 0 && *res_digits == 0)
     952              :     {
     953         1894 :         res_digits++;
     954         1894 :         res_weight--;
     955         1894 :         res_ndigits--;
     956              :     }
     957          858 :     while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
     958           12 :         res_ndigits--;
     959              : 
     960          846 :     if (res_ndigits == 0)
     961              :     {
     962          112 :         res_sign = NUMERIC_POS;
     963          112 :         res_weight = 0;
     964              :     }
     965              : 
     966          846 :     digitbuf_free(result->buf);
     967          846 :     result->buf = res_buf;
     968          846 :     result->digits = res_digits;
     969          846 :     result->ndigits = res_ndigits;
     970          846 :     result->weight = res_weight;
     971          846 :     result->rscale = global_rscale;
     972          846 :     result->sign = res_sign;
     973          846 :     result->dscale = var1->dscale + var2->dscale;
     974              : 
     975          846 :     return 0;
     976              : }
     977              : 
     978              : /*
     979              :  * Default scale selection for division
     980              :  *
     981              :  * Returns the appropriate display scale for the division result,
     982              :  * and sets global_rscale to the result scale to use during div_var.
     983              :  *
     984              :  * Note that this must be called before div_var.
     985              :  */
     986              : static int
     987          844 : select_div_scale(numeric *var1, numeric *var2, int *rscale)
     988              : {
     989              :     int         weight1,
     990              :                 weight2,
     991              :                 qweight,
     992              :                 i;
     993              :     NumericDigit firstdigit1,
     994              :                 firstdigit2;
     995              :     int         res_dscale;
     996              : 
     997              :     /*
     998              :      * The result scale of a division isn't specified in any SQL standard. For
     999              :      * PostgreSQL we select a display scale that will give at least
    1000              :      * NUMERIC_MIN_SIG_DIGITS significant digits, so that numeric gives a
    1001              :      * result no less accurate than float8; but use a scale not less than
    1002              :      * either input's display scale.
    1003              :      */
    1004              : 
    1005              :     /* Get the actual (normalized) weight and first digit of each input */
    1006              : 
    1007          844 :     weight1 = 0;                /* values to use if var1 is zero */
    1008          844 :     firstdigit1 = 0;
    1009          844 :     for (i = 0; i < var1->ndigits; i++)
    1010              :     {
    1011          786 :         firstdigit1 = var1->digits[i];
    1012          786 :         if (firstdigit1 != 0)
    1013              :         {
    1014          786 :             weight1 = var1->weight - i;
    1015          786 :             break;
    1016              :         }
    1017              :     }
    1018              : 
    1019          844 :     weight2 = 0;                /* values to use if var2 is zero */
    1020          844 :     firstdigit2 = 0;
    1021          844 :     for (i = 0; i < var2->ndigits; i++)
    1022              :     {
    1023          786 :         firstdigit2 = var2->digits[i];
    1024          786 :         if (firstdigit2 != 0)
    1025              :         {
    1026          786 :             weight2 = var2->weight - i;
    1027          786 :             break;
    1028              :         }
    1029              :     }
    1030              : 
    1031              :     /*
    1032              :      * Estimate weight of quotient.  If the two first digits are equal, we
    1033              :      * can't be sure, but assume that var1 is less than var2.
    1034              :      */
    1035          844 :     qweight = weight1 - weight2;
    1036          844 :     if (firstdigit1 <= firstdigit2)
    1037          508 :         qweight--;
    1038              : 
    1039              :     /* Select display scale */
    1040          844 :     res_dscale = NUMERIC_MIN_SIG_DIGITS - qweight;
    1041          844 :     res_dscale = Max(res_dscale, var1->dscale);
    1042          844 :     res_dscale = Max(res_dscale, var2->dscale);
    1043          844 :     res_dscale = Max(res_dscale, NUMERIC_MIN_DISPLAY_SCALE);
    1044          844 :     res_dscale = Min(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
    1045              : 
    1046              :     /* Select result scale */
    1047          844 :     *rscale = res_dscale + 4;
    1048              : 
    1049          844 :     return res_dscale;
    1050              : }
    1051              : 
    1052              : int
    1053          844 : PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result)
    1054              : {
    1055              :     NumericDigit *res_digits;
    1056              :     int         res_ndigits;
    1057              :     int         res_sign;
    1058              :     int         res_weight;
    1059              :     numeric     dividend;
    1060              :     numeric     divisor[10];
    1061              :     int         ndigits_tmp;
    1062              :     int         weight_tmp;
    1063              :     int         rscale_tmp;
    1064              :     int         ri;
    1065              :     long        guess;
    1066              :     long        first_have;
    1067              :     long        first_div;
    1068              :     int         first_nextdigit;
    1069          844 :     int         stat = 0;
    1070              :     int         rscale;
    1071          844 :     int         res_dscale = select_div_scale(var1, var2, &rscale);
    1072          844 :     int         err = -1;
    1073              :     NumericDigit *tmp_buf;
    1074              : 
    1075              :     /*
    1076              :      * First of all division by zero check
    1077              :      */
    1078          844 :     ndigits_tmp = var2->ndigits + 1;
    1079          844 :     if (ndigits_tmp == 1)
    1080              :     {
    1081           58 :         errno = PGTYPES_NUM_DIVIDE_ZERO;
    1082           58 :         return -1;
    1083              :     }
    1084              : 
    1085              :     /*
    1086              :      * Determine the result sign, weight and number of digits to calculate
    1087              :      */
    1088          786 :     if (var1->sign == var2->sign)
    1089          466 :         res_sign = NUMERIC_POS;
    1090              :     else
    1091          320 :         res_sign = NUMERIC_NEG;
    1092          786 :     res_weight = var1->weight - var2->weight + 1;
    1093          786 :     res_ndigits = rscale + res_weight;
    1094          786 :     if (res_ndigits <= 0)
    1095            0 :         res_ndigits = 1;
    1096              : 
    1097              :     /*
    1098              :      * Now result zero check
    1099              :      */
    1100          786 :     if (var1->ndigits == 0)
    1101              :     {
    1102           54 :         zero_var(result);
    1103           54 :         result->rscale = rscale;
    1104           54 :         return 0;
    1105              :     }
    1106              : 
    1107              :     /*
    1108              :      * Initialize local variables
    1109              :      */
    1110          732 :     init_var(&dividend);
    1111         7320 :     for (int i = 1; i < 10; i++)
    1112         6588 :         init_var(&divisor[i]);
    1113              : 
    1114              :     /*
    1115              :      * Make a copy of the divisor which has one leading zero digit
    1116              :      */
    1117          732 :     divisor[1].ndigits = ndigits_tmp;
    1118          732 :     divisor[1].rscale = var2->ndigits;
    1119          732 :     divisor[1].sign = NUMERIC_POS;
    1120          732 :     divisor[1].buf = digitbuf_alloc(ndigits_tmp);
    1121          732 :     if (divisor[1].buf == NULL)
    1122            0 :         goto done;
    1123          732 :     divisor[1].digits = divisor[1].buf;
    1124          732 :     divisor[1].digits[0] = 0;
    1125          732 :     memcpy(&(divisor[1].digits[1]), var2->digits, ndigits_tmp - 1);
    1126              : 
    1127              :     /*
    1128              :      * Make a copy of the dividend
    1129              :      */
    1130          732 :     dividend.ndigits = var1->ndigits;
    1131          732 :     dividend.weight = 0;
    1132          732 :     dividend.rscale = var1->ndigits;
    1133          732 :     dividend.sign = NUMERIC_POS;
    1134          732 :     dividend.buf = digitbuf_alloc(var1->ndigits);
    1135          732 :     if (dividend.buf == NULL)
    1136            0 :         goto done;
    1137          732 :     dividend.digits = dividend.buf;
    1138          732 :     memcpy(dividend.digits, var1->digits, var1->ndigits);
    1139              : 
    1140              :     /*
    1141              :      * Setup the result. Do the allocation in a temporary buffer first, so we
    1142              :      * don't free result->buf unless we have successfully allocated a buffer
    1143              :      * to replace it with.
    1144              :      */
    1145          732 :     tmp_buf = digitbuf_alloc(res_ndigits + 2);
    1146          732 :     if (tmp_buf == NULL)
    1147            0 :         goto done;
    1148          732 :     digitbuf_free(result->buf);
    1149          732 :     result->buf = tmp_buf;
    1150          732 :     res_digits = result->buf;
    1151          732 :     result->digits = res_digits;
    1152          732 :     result->ndigits = res_ndigits;
    1153          732 :     result->weight = res_weight;
    1154          732 :     result->rscale = rscale;
    1155          732 :     result->sign = res_sign;
    1156          732 :     res_digits[0] = 0;
    1157              : 
    1158          732 :     first_div = divisor[1].digits[1] * 10;
    1159          732 :     if (ndigits_tmp > 2)
    1160          516 :         first_div += divisor[1].digits[2];
    1161              : 
    1162          732 :     first_have = 0;
    1163          732 :     first_nextdigit = 0;
    1164              : 
    1165          732 :     weight_tmp = 1;
    1166          732 :     rscale_tmp = divisor[1].rscale;
    1167              : 
    1168        27580 :     for (ri = 0; ri <= res_ndigits; ri++)
    1169              :     {
    1170        27114 :         first_have = first_have * 10;
    1171        27114 :         if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)
    1172        25166 :             first_have += dividend.digits[first_nextdigit];
    1173        27114 :         first_nextdigit++;
    1174              : 
    1175        27114 :         guess = (first_have * 10) / first_div + 1;
    1176        27114 :         if (guess > 9)
    1177         2946 :             guess = 9;
    1178              : 
    1179        50012 :         while (guess > 0)
    1180              :         {
    1181        46340 :             if (divisor[guess].buf == NULL)
    1182              :             {
    1183              :                 int         i;
    1184         4554 :                 long        sum = 0;
    1185              : 
    1186         4554 :                 memcpy(&divisor[guess], &divisor[1], sizeof(numeric));
    1187         4554 :                 divisor[guess].buf = digitbuf_alloc(divisor[guess].ndigits);
    1188         4554 :                 if (divisor[guess].buf == NULL)
    1189            0 :                     goto done;
    1190         4554 :                 divisor[guess].digits = divisor[guess].buf;
    1191        41030 :                 for (i = divisor[1].ndigits - 1; i >= 0; i--)
    1192              :                 {
    1193        36476 :                     sum += divisor[1].digits[i] * guess;
    1194        36476 :                     divisor[guess].digits[i] = sum % 10;
    1195        36476 :                     sum /= 10;
    1196              :                 }
    1197              :             }
    1198              : 
    1199        46340 :             divisor[guess].weight = weight_tmp;
    1200        46340 :             divisor[guess].rscale = rscale_tmp;
    1201              : 
    1202        46340 :             stat = cmp_abs(&dividend, &divisor[guess]);
    1203        46340 :             if (stat >= 0)
    1204        23442 :                 break;
    1205              : 
    1206        22898 :             guess--;
    1207              :         }
    1208              : 
    1209        27114 :         res_digits[ri + 1] = guess;
    1210        27114 :         if (stat == 0)
    1211              :         {
    1212          266 :             ri++;
    1213          266 :             break;
    1214              :         }
    1215              : 
    1216        26848 :         weight_tmp--;
    1217        26848 :         rscale_tmp++;
    1218              : 
    1219        26848 :         if (guess == 0)
    1220         3672 :             continue;
    1221              : 
    1222        23176 :         if (sub_abs(&dividend, &divisor[guess], &dividend) != 0)
    1223            0 :             goto done;
    1224              : 
    1225        23176 :         first_nextdigit = dividend.weight - weight_tmp;
    1226        23176 :         first_have = 0;
    1227        23176 :         if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)
    1228        15920 :             first_have = dividend.digits[first_nextdigit];
    1229        23176 :         first_nextdigit++;
    1230              :     }
    1231              : 
    1232          732 :     result->ndigits = ri + 1;
    1233          732 :     if (ri == res_ndigits + 1)
    1234              :     {
    1235          466 :         int         carry = (res_digits[ri] > 4) ? 1 : 0;
    1236              : 
    1237          466 :         result->ndigits = ri;
    1238          466 :         res_digits[ri] = 0;
    1239              : 
    1240          772 :         while (carry && ri > 0)
    1241              :         {
    1242          306 :             carry += res_digits[--ri];
    1243          306 :             res_digits[ri] = carry % 10;
    1244          306 :             carry /= 10;
    1245              :         }
    1246              :     }
    1247              : 
    1248         1780 :     while (result->ndigits > 0 && *(result->digits) == 0)
    1249              :     {
    1250         1048 :         (result->digits)++;
    1251         1048 :         (result->weight)--;
    1252         1048 :         (result->ndigits)--;
    1253              :     }
    1254          888 :     while (result->ndigits > 0 && result->digits[result->ndigits - 1] == 0)
    1255          156 :         (result->ndigits)--;
    1256          732 :     if (result->ndigits == 0)
    1257            0 :         result->sign = NUMERIC_POS;
    1258              : 
    1259          732 :     result->dscale = res_dscale;
    1260          732 :     err = 0;                    /* if we've made it this far, return success */
    1261              : 
    1262          732 : done:
    1263              : 
    1264              :     /*
    1265              :      * Tidy up
    1266              :      */
    1267          732 :     if (dividend.buf != NULL)
    1268          732 :         digitbuf_free(dividend.buf);
    1269              : 
    1270         7320 :     for (int i = 1; i < 10; i++)
    1271              :     {
    1272         6588 :         if (divisor[i].buf != NULL)
    1273         5286 :             digitbuf_free(divisor[i].buf);
    1274              :     }
    1275              : 
    1276          732 :     return err;
    1277              : }
    1278              : 
    1279              : 
    1280              : int
    1281          566 : PGTYPESnumeric_cmp(numeric *var1, numeric *var2)
    1282              : {
    1283              :     /* use cmp_abs function to calculate the result */
    1284              : 
    1285              :     /* both are positive: normal comparison with cmp_abs */
    1286          566 :     if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_POS)
    1287          276 :         return cmp_abs(var1, var2);
    1288              : 
    1289              :     /* both are negative: return the inverse of the normal comparison */
    1290          290 :     if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_NEG)
    1291              :     {
    1292              :         /*
    1293              :          * instead of inverting the result, we invert the parameter ordering
    1294              :          */
    1295           64 :         return cmp_abs(var2, var1);
    1296              :     }
    1297              : 
    1298              :     /* one is positive, one is negative: trivial */
    1299          226 :     if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_NEG)
    1300           80 :         return 1;
    1301          146 :     if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_POS)
    1302           88 :         return -1;
    1303              : 
    1304           58 :     errno = PGTYPES_NUM_BAD_NUMERIC;
    1305           58 :     return INT_MAX;
    1306              : }
    1307              : 
    1308              : int
    1309         1872 : PGTYPESnumeric_from_int(signed int int_val, numeric *var)
    1310              : {
    1311              :     /* implicit conversion */
    1312         1872 :     signed long int long_int = int_val;
    1313              : 
    1314         1872 :     return PGTYPESnumeric_from_long(long_int, var);
    1315              : }
    1316              : 
    1317              : int
    1318         1916 : PGTYPESnumeric_from_long(signed long int long_val, numeric *var)
    1319              : {
    1320              :     /* calculate the size of the long int number */
    1321              :     /* a number n needs log_10 n digits */
    1322              : 
    1323              :     /*
    1324              :      * however we multiply by 10 each time and compare instead of calculating
    1325              :      * the logarithm
    1326              :      */
    1327              : 
    1328         1916 :     int         size = 0;
    1329              :     int         i;
    1330         1916 :     signed long int abs_long_val = long_val;
    1331              :     signed long int extract;
    1332              :     signed long int reach_limit;
    1333              : 
    1334         1916 :     if (abs_long_val < 0)
    1335              :     {
    1336           24 :         abs_long_val *= -1;
    1337           24 :         var->sign = NUMERIC_NEG;
    1338              :     }
    1339              :     else
    1340         1892 :         var->sign = NUMERIC_POS;
    1341              : 
    1342         1916 :     reach_limit = 1;
    1343              :     do
    1344              :     {
    1345        12764 :         size++;
    1346        12764 :         reach_limit *= 10;
    1347        12764 :     } while (reach_limit - 1 < abs_long_val && reach_limit <= LONG_MAX / 10);
    1348              : 
    1349         1916 :     if (reach_limit > LONG_MAX / 10)
    1350              :     {
    1351              :         /* add the first digit and a .0 */
    1352            0 :         size += 2;
    1353              :     }
    1354              :     else
    1355              :     {
    1356              :         /* always add a .0 */
    1357         1916 :         size++;
    1358         1916 :         reach_limit /= 10;
    1359              :     }
    1360              : 
    1361         1916 :     if (alloc_var(var, size) < 0)
    1362            0 :         return -1;
    1363              : 
    1364         1916 :     var->rscale = 1;
    1365         1916 :     var->dscale = 1;
    1366         1916 :     var->weight = size - 2;
    1367              : 
    1368         1916 :     i = 0;
    1369              :     do
    1370              :     {
    1371        12748 :         extract = abs_long_val - (abs_long_val % reach_limit);
    1372        12748 :         var->digits[i] = extract / reach_limit;
    1373        12748 :         abs_long_val -= extract;
    1374        12748 :         i++;
    1375        12748 :         reach_limit /= 10;
    1376              : 
    1377              :         /*
    1378              :          * we can abandon if abs_long_val reaches 0, because the memory is
    1379              :          * initialized properly and filled with '0', so converting 10000 in
    1380              :          * only one step is no problem
    1381              :          */
    1382        12748 :     } while (abs_long_val > 0);
    1383              : 
    1384         1916 :     return 0;
    1385              : }
    1386              : 
    1387              : int
    1388         4102 : PGTYPESnumeric_copy(numeric *src, numeric *dst)
    1389              : {
    1390              :     int         i;
    1391              : 
    1392         4102 :     if (dst == NULL)
    1393            0 :         return -1;
    1394         4102 :     zero_var(dst);
    1395              : 
    1396         4102 :     dst->weight = src->weight;
    1397         4102 :     dst->rscale = src->rscale;
    1398         4102 :     dst->dscale = src->dscale;
    1399         4102 :     dst->sign = src->sign;
    1400              : 
    1401         4102 :     if (alloc_var(dst, src->ndigits) != 0)
    1402            0 :         return -1;
    1403              : 
    1404       134404 :     for (i = 0; i < src->ndigits; i++)
    1405       130302 :         dst->digits[i] = src->digits[i];
    1406              : 
    1407         4102 :     return 0;
    1408              : }
    1409              : 
    1410              : int
    1411            0 : PGTYPESnumeric_from_double(double d, numeric *dst)
    1412              : {
    1413              :     char        buffer[DBL_DIG + 100];
    1414              :     numeric    *tmp;
    1415              :     int         i;
    1416              : 
    1417            0 :     if (sprintf(buffer, "%.*g", DBL_DIG, d) <= 0)
    1418            0 :         return -1;
    1419              : 
    1420            0 :     if ((tmp = PGTYPESnumeric_from_asc(buffer, NULL)) == NULL)
    1421            0 :         return -1;
    1422            0 :     i = PGTYPESnumeric_copy(tmp, dst);
    1423            0 :     PGTYPESnumeric_free(tmp);
    1424            0 :     if (i != 0)
    1425            0 :         return -1;
    1426              : 
    1427            0 :     errno = 0;
    1428            0 :     return 0;
    1429              : }
    1430              : 
    1431              : static int
    1432           56 : numericvar_to_double(numeric *var, double *dp)
    1433              : {
    1434              :     char       *tmp;
    1435              :     double      val;
    1436              :     char       *endptr;
    1437           56 :     numeric    *varcopy = PGTYPESnumeric_new();
    1438              : 
    1439           56 :     if (varcopy == NULL)
    1440            0 :         return -1;
    1441              : 
    1442           56 :     if (PGTYPESnumeric_copy(var, varcopy) < 0)
    1443              :     {
    1444            0 :         PGTYPESnumeric_free(varcopy);
    1445            0 :         return -1;
    1446              :     }
    1447              : 
    1448           56 :     tmp = get_str_from_var(varcopy, varcopy->dscale);
    1449           56 :     PGTYPESnumeric_free(varcopy);
    1450              : 
    1451           56 :     if (tmp == NULL)
    1452            0 :         return -1;
    1453              : 
    1454              :     /*
    1455              :      * strtod does not reset errno to 0 in case of success.
    1456              :      */
    1457           56 :     errno = 0;
    1458           56 :     val = strtod(tmp, &endptr);
    1459           56 :     if (errno == ERANGE)
    1460              :     {
    1461            4 :         free(tmp);
    1462            4 :         if (val == 0)
    1463            0 :             errno = PGTYPES_NUM_UNDERFLOW;
    1464              :         else
    1465            4 :             errno = PGTYPES_NUM_OVERFLOW;
    1466            4 :         return -1;
    1467              :     }
    1468              : 
    1469              :     /* can't free tmp yet, endptr points still into it */
    1470           52 :     if (*endptr != '\0')
    1471              :     {
    1472              :         /* shouldn't happen ... */
    1473            0 :         free(tmp);
    1474            0 :         errno = PGTYPES_NUM_BAD_NUMERIC;
    1475            0 :         return -1;
    1476              :     }
    1477           52 :     free(tmp);
    1478           52 :     *dp = val;
    1479           52 :     return 0;
    1480              : }
    1481              : 
    1482              : int
    1483           56 : PGTYPESnumeric_to_double(numeric *nv, double *dp)
    1484              : {
    1485              :     double      tmp;
    1486              : 
    1487           56 :     if (numericvar_to_double(nv, &tmp) != 0)
    1488            4 :         return -1;
    1489           52 :     *dp = tmp;
    1490           52 :     return 0;
    1491              : }
    1492              : 
    1493              : int
    1494           66 : PGTYPESnumeric_to_int(numeric *nv, int *ip)
    1495              : {
    1496              :     long        l;
    1497              :     int         i;
    1498              : 
    1499           66 :     if ((i = PGTYPESnumeric_to_long(nv, &l)) != 0)
    1500           14 :         return i;
    1501              : 
    1502              : /* silence compilers that might complain about useless tests */
    1503              : #if SIZEOF_LONG > SIZEOF_INT
    1504              : 
    1505           52 :     if (l < INT_MIN || l > INT_MAX)
    1506              :     {
    1507            0 :         errno = PGTYPES_NUM_OVERFLOW;
    1508            0 :         return -1;
    1509              :     }
    1510              : 
    1511              : #endif
    1512              : 
    1513           52 :     *ip = (int) l;
    1514           52 :     return 0;
    1515              : }
    1516              : 
    1517              : int
    1518          128 : PGTYPESnumeric_to_long(numeric *nv, long *lp)
    1519              : {
    1520          128 :     char       *s = PGTYPESnumeric_to_asc(nv, 0);
    1521              :     char       *endptr;
    1522              : 
    1523          128 :     if (s == NULL)
    1524            0 :         return -1;
    1525              : 
    1526          128 :     errno = 0;
    1527          128 :     *lp = strtol(s, &endptr, 10);
    1528          128 :     if (endptr == s)
    1529              :     {
    1530              :         /* this should not happen actually */
    1531            0 :         free(s);
    1532            0 :         return -1;
    1533              :     }
    1534          128 :     free(s);
    1535          128 :     if (errno == ERANGE)
    1536              :     {
    1537           28 :         if (*lp == LONG_MIN)
    1538            0 :             errno = PGTYPES_NUM_UNDERFLOW;
    1539              :         else
    1540           28 :             errno = PGTYPES_NUM_OVERFLOW;
    1541           28 :         return -1;
    1542              :     }
    1543          100 :     return 0;
    1544              : }
    1545              : 
    1546              : int
    1547         3468 : PGTYPESnumeric_to_decimal(numeric *src, decimal *dst)
    1548              : {
    1549              :     int         i;
    1550              : 
    1551         3468 :     if (src->ndigits > DECSIZE)
    1552              :     {
    1553          324 :         errno = PGTYPES_NUM_OVERFLOW;
    1554          324 :         return -1;
    1555              :     }
    1556              : 
    1557         3144 :     dst->weight = src->weight;
    1558         3144 :     dst->rscale = src->rscale;
    1559         3144 :     dst->dscale = src->dscale;
    1560         3144 :     dst->sign = src->sign;
    1561         3144 :     dst->ndigits = src->ndigits;
    1562              : 
    1563        29812 :     for (i = 0; i < src->ndigits; i++)
    1564        26668 :         dst->digits[i] = src->digits[i];
    1565              : 
    1566         3144 :     return 0;
    1567              : }
    1568              : 
    1569              : int
    1570         5926 : PGTYPESnumeric_from_decimal(decimal *src, numeric *dst)
    1571              : {
    1572              :     int         i;
    1573              : 
    1574         5926 :     zero_var(dst);
    1575              : 
    1576         5926 :     dst->weight = src->weight;
    1577         5926 :     dst->rscale = src->rscale;
    1578         5926 :     dst->dscale = src->dscale;
    1579         5926 :     dst->sign = src->sign;
    1580              : 
    1581         5926 :     if (alloc_var(dst, src->ndigits) != 0)
    1582            0 :         return -1;
    1583              : 
    1584        41462 :     for (i = 0; i < src->ndigits; i++)
    1585        35536 :         dst->digits[i] = src->digits[i];
    1586              : 
    1587         5926 :     return 0;
    1588              : }
        

Generated by: LCOV version 2.0-1