LCOV - code coverage report
Current view: top level - src/backend/utils/adt - cash.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 83.3 % 419 349
Test Date: 2026-04-07 14:16:30 Functions: 95.3 % 43 41
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * cash.c
       3              :  * Written by D'Arcy J.M. Cain
       4              :  * darcy@druid.net
       5              :  * http://www.druid.net/darcy/
       6              :  *
       7              :  * Functions to allow input and output of money normally but store
       8              :  * and handle it as 64 bit ints
       9              :  *
      10              :  * A slightly modified version of this file and a discussion of the
      11              :  * workings can be found in the book "Software Solutions in C" by
      12              :  * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
      13              :  * this version handles 64 bit numbers and so can hold values up to
      14              :  * $92,233,720,368,547,758.07.
      15              :  *
      16              :  * src/backend/utils/adt/cash.c
      17              :  */
      18              : 
      19              : #include "postgres.h"
      20              : 
      21              : #include <limits.h>
      22              : #include <ctype.h>
      23              : #include <math.h>
      24              : 
      25              : #include "common/int.h"
      26              : #include "libpq/pqformat.h"
      27              : #include "nodes/miscnodes.h"
      28              : #include "utils/builtins.h"
      29              : #include "utils/cash.h"
      30              : #include "utils/float.h"
      31              : #include "utils/numeric.h"
      32              : #include "utils/pg_locale.h"
      33              : 
      34              : 
      35              : /*************************************************************************
      36              :  * Private routines
      37              :  ************************************************************************/
      38              : 
      39              : static void
      40           16 : append_num_word(StringInfo buf, Cash value)
      41              : {
      42              :     static const char *const small[] = {
      43              :         "zero", "one", "two", "three", "four", "five", "six", "seven",
      44              :         "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
      45              :         "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
      46              :         "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
      47              :     };
      48           16 :     const char *const *big = small + 18;
      49           16 :     int         tu = value % 100;
      50              : 
      51              :     /* deal with the simple cases first */
      52           16 :     if (value <= 20)
      53              :     {
      54            4 :         appendStringInfoString(buf, small[value]);
      55            4 :         return;
      56              :     }
      57              : 
      58              :     /* is it an even multiple of 100? */
      59           12 :     if (!tu)
      60              :     {
      61            0 :         appendStringInfo(buf, "%s hundred", small[value / 100]);
      62            0 :         return;
      63              :     }
      64              : 
      65              :     /* more than 99? */
      66           12 :     if (value > 99)
      67              :     {
      68              :         /* is it an even multiple of 10 other than 10? */
      69            8 :         if (value % 10 == 0 && tu > 10)
      70            0 :             appendStringInfo(buf, "%s hundred %s",
      71            0 :                              small[value / 100], big[tu / 10]);
      72            8 :         else if (tu < 20)
      73            0 :             appendStringInfo(buf, "%s hundred and %s",
      74            0 :                              small[value / 100], small[tu]);
      75              :         else
      76            8 :             appendStringInfo(buf, "%s hundred %s %s",
      77            8 :                              small[value / 100], big[tu / 10], small[tu % 10]);
      78              :     }
      79              :     else
      80              :     {
      81              :         /* is it an even multiple of 10 other than 10? */
      82            4 :         if (value % 10 == 0 && tu > 10)
      83            0 :             appendStringInfoString(buf, big[tu / 10]);
      84            4 :         else if (tu < 20)
      85            0 :             appendStringInfoString(buf, small[tu]);
      86              :         else
      87            4 :             appendStringInfo(buf, "%s %s", big[tu / 10], small[tu % 10]);
      88              :     }
      89              : }
      90              : 
      91              : static inline Cash
      92           20 : cash_pl_cash(Cash c1, Cash c2)
      93              : {
      94              :     Cash        res;
      95              : 
      96           20 :     if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
      97            4 :         ereport(ERROR,
      98              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
      99              :                  errmsg("money out of range")));
     100              : 
     101           16 :     return res;
     102              : }
     103              : 
     104              : static inline Cash
     105           12 : cash_mi_cash(Cash c1, Cash c2)
     106              : {
     107              :     Cash        res;
     108              : 
     109           12 :     if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
     110            4 :         ereport(ERROR,
     111              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     112              :                  errmsg("money out of range")));
     113              : 
     114            8 :     return res;
     115              : }
     116              : 
     117              : static inline Cash
     118           32 : cash_mul_float8(Cash c, float8 f)
     119              : {
     120           32 :     float8      res = rint(float8_mul((float8) c, f));
     121              : 
     122           32 :     if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
     123           16 :         ereport(ERROR,
     124              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     125              :                  errmsg("money out of range")));
     126              : 
     127           16 :     return (Cash) res;
     128              : }
     129              : 
     130              : static inline Cash
     131           22 : cash_div_float8(Cash c, float8 f)
     132              : {
     133           22 :     float8      res = rint(float8_div((float8) c, f));
     134              : 
     135           22 :     if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
     136            4 :         ereport(ERROR,
     137              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     138              :                  errmsg("money out of range")));
     139              : 
     140           18 :     return (Cash) res;
     141              : }
     142              : 
     143              : static inline Cash
     144           28 : cash_mul_int64(Cash c, int64 i)
     145              : {
     146              :     Cash        res;
     147              : 
     148           28 :     if (unlikely(pg_mul_s64_overflow(c, i, &res)))
     149            4 :         ereport(ERROR,
     150              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     151              :                  errmsg("money out of range")));
     152              : 
     153           24 :     return res;
     154              : }
     155              : 
     156              : static inline Cash
     157           46 : cash_div_int64(Cash c, int64 i)
     158              : {
     159           46 :     if (unlikely(i == 0))
     160            4 :         ereport(ERROR,
     161              :                 (errcode(ERRCODE_DIVISION_BY_ZERO),
     162              :                  errmsg("division by zero")));
     163              : 
     164           42 :     return c / i;
     165              : }
     166              : 
     167              : /* cash_in()
     168              :  * Convert a string to a cash data type.
     169              :  * Format is [$]###[,]###[.##]
     170              :  * Examples: 123.45 $123.45 $123,456.78
     171              :  *
     172              :  */
     173              : Datum
     174          879 : cash_in(PG_FUNCTION_ARGS)
     175              : {
     176          879 :     char       *str = PG_GETARG_CSTRING(0);
     177          879 :     Node       *escontext = fcinfo->context;
     178              :     Cash        result;
     179          879 :     Cash        value = 0;
     180          879 :     Cash        dec = 0;
     181          879 :     Cash        sgn = 1;
     182          879 :     bool        seen_dot = false;
     183          879 :     const char *s = str;
     184              :     int         fpoint;
     185              :     char        dsymbol;
     186              :     const char *ssymbol,
     187              :                *psymbol,
     188              :                *nsymbol,
     189              :                *csymbol;
     190          879 :     struct lconv *lconvert = PGLC_localeconv();
     191              : 
     192              :     /*
     193              :      * frac_digits will be CHAR_MAX in some locales, notably C.  However, just
     194              :      * testing for == CHAR_MAX is risky, because of compilers like gcc that
     195              :      * "helpfully" let you alter the platform-standard definition of whether
     196              :      * char is signed or not.  If we are so unfortunate as to get compiled
     197              :      * with a nonstandard -fsigned-char or -funsigned-char switch, then our
     198              :      * idea of CHAR_MAX will not agree with libc's. The safest course is not
     199              :      * to test for CHAR_MAX at all, but to impose a range check for plausible
     200              :      * frac_digits values.
     201              :      */
     202          879 :     fpoint = lconvert->frac_digits;
     203          879 :     if (fpoint < 0 || fpoint > 10)
     204          879 :         fpoint = 2;             /* best guess in this case, I think */
     205              : 
     206              :     /* we restrict dsymbol to be a single byte, but not the other symbols */
     207          879 :     if (*lconvert->mon_decimal_point != '\0' &&
     208            0 :         lconvert->mon_decimal_point[1] == '\0')
     209            0 :         dsymbol = *lconvert->mon_decimal_point;
     210              :     else
     211          879 :         dsymbol = '.';
     212          879 :     if (*lconvert->mon_thousands_sep != '\0')
     213            0 :         ssymbol = lconvert->mon_thousands_sep;
     214              :     else                        /* ssymbol should not equal dsymbol */
     215          879 :         ssymbol = (dsymbol != ',') ? "," : ".";
     216          879 :     csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
     217          879 :     psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
     218          879 :     nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
     219              : 
     220              : #ifdef CASHDEBUG
     221              :     printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
     222              :            fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
     223              : #endif
     224              : 
     225              :     /* we need to add all sorts of checking here.  For now just */
     226              :     /* strip all leading whitespace and any leading currency symbol */
     227          879 :     while (isspace((unsigned char) *s))
     228            0 :         s++;
     229          879 :     if (strncmp(s, csymbol, strlen(csymbol)) == 0)
     230           80 :         s += strlen(csymbol);
     231          879 :     while (isspace((unsigned char) *s))
     232            0 :         s++;
     233              : 
     234              : #ifdef CASHDEBUG
     235              :     printf("cashin- string is '%s'\n", s);
     236              : #endif
     237              : 
     238              :     /* a leading minus or paren signifies a negative number */
     239              :     /* again, better heuristics needed */
     240              :     /* XXX - doesn't properly check for balanced parens - djmc */
     241          879 :     if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
     242              :     {
     243          323 :         sgn = -1;
     244          323 :         s += strlen(nsymbol);
     245              :     }
     246          556 :     else if (*s == '(')
     247              :     {
     248            8 :         sgn = -1;
     249            8 :         s++;
     250              :     }
     251          548 :     else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
     252            0 :         s += strlen(psymbol);
     253              : 
     254              : #ifdef CASHDEBUG
     255              :     printf("cashin- string is '%s'\n", s);
     256              : #endif
     257              : 
     258              :     /* allow whitespace and currency symbol after the sign, too */
     259          879 :     while (isspace((unsigned char) *s))
     260            0 :         s++;
     261          879 :     if (strncmp(s, csymbol, strlen(csymbol)) == 0)
     262            4 :         s += strlen(csymbol);
     263          879 :     while (isspace((unsigned char) *s))
     264            0 :         s++;
     265              : 
     266              : #ifdef CASHDEBUG
     267              :     printf("cashin- string is '%s'\n", s);
     268              : #endif
     269              : 
     270              :     /*
     271              :      * We accumulate the absolute amount in "value" and then apply the sign at
     272              :      * the end.  (The sign can appear before or after the digits, so it would
     273              :      * be more complicated to do otherwise.)  Because of the larger range of
     274              :      * negative signed integers, we build "value" in the negative and then
     275              :      * flip the sign at the end, catching most-negative-number overflow if
     276              :      * necessary.
     277              :      */
     278              : 
     279         8718 :     for (; *s; s++)
     280              :     {
     281              :         /*
     282              :          * We look for digits as long as we have found less than the required
     283              :          * number of decimal places.
     284              :          */
     285         7895 :         if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
     286         7064 :         {
     287         7076 :             int8        digit = *s - '0';
     288              : 
     289        14144 :             if (pg_mul_s64_overflow(value, 10, &value) ||
     290         7068 :                 pg_sub_s64_overflow(value, digit, &value))
     291           12 :                 ereturn(escontext, (Datum) 0,
     292              :                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     293              :                          errmsg("value \"%s\" is out of range for type %s",
     294              :                                 str, "money")));
     295              : 
     296         7064 :             if (seen_dot)
     297         1530 :                 dec++;
     298              :         }
     299              :         /* decimal point? then start counting fractions... */
     300          819 :         else if (*s == dsymbol && !seen_dot)
     301              :         {
     302          771 :             seen_dot = true;
     303              :         }
     304              :         /* ignore if "thousands" separator, else we're done */
     305           48 :         else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
     306            4 :             s += strlen(ssymbol) - 1;
     307              :         else
     308           44 :             break;
     309              :     }
     310              : 
     311              :     /* round off if there's another digit */
     312          867 :     if (isdigit((unsigned char) *s) && *s >= '5')
     313              :     {
     314              :         /* remember we build the value in the negative */
     315           20 :         if (pg_sub_s64_overflow(value, 1, &value))
     316            4 :             ereturn(escontext, (Datum) 0,
     317              :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     318              :                      errmsg("value \"%s\" is out of range for type %s",
     319              :                             str, "money")));
     320              :     }
     321              : 
     322              :     /* adjust for less than required decimal places */
     323         1055 :     for (; dec < fpoint; dec++)
     324              :     {
     325          208 :         if (pg_mul_s64_overflow(value, 10, &value))
     326           16 :             ereturn(escontext, (Datum) 0,
     327              :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     328              :                      errmsg("value \"%s\" is out of range for type %s",
     329              :                             str, "money")));
     330              :     }
     331              : 
     332              :     /*
     333              :      * should only be trailing digits followed by whitespace, right paren,
     334              :      * trailing sign, and/or trailing currency symbol
     335              :      */
     336          871 :     while (isdigit((unsigned char) *s))
     337           24 :         s++;
     338              : 
     339          855 :     while (*s)
     340              :     {
     341           16 :         if (isspace((unsigned char) *s) || *s == ')')
     342            8 :             s++;
     343            8 :         else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
     344              :         {
     345            0 :             sgn = -1;
     346            0 :             s += strlen(nsymbol);
     347              :         }
     348            8 :         else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
     349            0 :             s += strlen(psymbol);
     350            8 :         else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
     351            0 :             s += strlen(csymbol);
     352              :         else
     353            8 :             ereturn(escontext, (Datum) 0,
     354              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     355              :                      errmsg("invalid input syntax for type %s: \"%s\"",
     356              :                             "money", str)));
     357              :     }
     358              : 
     359              :     /*
     360              :      * If the value is supposed to be positive, flip the sign, but check for
     361              :      * the most negative number.
     362              :      */
     363          839 :     if (sgn > 0)
     364              :     {
     365          524 :         if (value == PG_INT64_MIN)
     366            8 :             ereturn(escontext, (Datum) 0,
     367              :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     368              :                      errmsg("value \"%s\" is out of range for type %s",
     369              :                             str, "money")));
     370          516 :         result = -value;
     371              :     }
     372              :     else
     373          315 :         result = value;
     374              : 
     375              : #ifdef CASHDEBUG
     376              :     printf("cashin- result is " INT64_FORMAT "\n", result);
     377              : #endif
     378              : 
     379          831 :     PG_RETURN_CASH(result);
     380              : }
     381              : 
     382              : 
     383              : /* cash_out()
     384              :  * Function to convert cash to a dollars and cents representation, using
     385              :  * the lc_monetary locale's formatting.
     386              :  */
     387              : Datum
     388          265 : cash_out(PG_FUNCTION_ARGS)
     389              : {
     390          265 :     Cash        value = PG_GETARG_CASH(0);
     391              :     uint64      uvalue;
     392              :     char       *result;
     393              :     char        buf[128];
     394              :     char       *bufptr;
     395              :     int         digit_pos;
     396              :     int         points,
     397              :                 mon_group;
     398              :     char        dsymbol;
     399              :     const char *ssymbol,
     400              :                *csymbol,
     401              :                *signsymbol;
     402              :     char        sign_posn,
     403              :                 cs_precedes,
     404              :                 sep_by_space;
     405          265 :     struct lconv *lconvert = PGLC_localeconv();
     406              : 
     407              :     /* see comments about frac_digits in cash_in() */
     408          265 :     points = lconvert->frac_digits;
     409          265 :     if (points < 0 || points > 10)
     410          265 :         points = 2;             /* best guess in this case, I think */
     411              : 
     412              :     /*
     413              :      * As with frac_digits, must apply a range check to mon_grouping to avoid
     414              :      * being fooled by variant CHAR_MAX values.
     415              :      */
     416          265 :     mon_group = *lconvert->mon_grouping;
     417          265 :     if (mon_group <= 0 || mon_group > 6)
     418          265 :         mon_group = 3;
     419              : 
     420              :     /* we restrict dsymbol to be a single byte, but not the other symbols */
     421          265 :     if (*lconvert->mon_decimal_point != '\0' &&
     422            0 :         lconvert->mon_decimal_point[1] == '\0')
     423            0 :         dsymbol = *lconvert->mon_decimal_point;
     424              :     else
     425          265 :         dsymbol = '.';
     426          265 :     if (*lconvert->mon_thousands_sep != '\0')
     427            0 :         ssymbol = lconvert->mon_thousands_sep;
     428              :     else                        /* ssymbol should not equal dsymbol */
     429          265 :         ssymbol = (dsymbol != ',') ? "," : ".";
     430          265 :     csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
     431              : 
     432          265 :     if (value < 0)
     433              :     {
     434              :         /* set up formatting data */
     435           56 :         signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
     436           56 :         sign_posn = lconvert->n_sign_posn;
     437           56 :         cs_precedes = lconvert->n_cs_precedes;
     438           56 :         sep_by_space = lconvert->n_sep_by_space;
     439              :     }
     440              :     else
     441              :     {
     442          209 :         signsymbol = lconvert->positive_sign;
     443          209 :         sign_posn = lconvert->p_sign_posn;
     444          209 :         cs_precedes = lconvert->p_cs_precedes;
     445          209 :         sep_by_space = lconvert->p_sep_by_space;
     446              :     }
     447              : 
     448              :     /* make the amount positive for digit-reconstruction loop */
     449          265 :     uvalue = pg_abs_s64(value);
     450              : 
     451              :     /* we build the digits+decimal-point+sep string right-to-left in buf[] */
     452          265 :     bufptr = buf + sizeof(buf) - 1;
     453          265 :     *bufptr = '\0';
     454              : 
     455              :     /*
     456              :      * Generate digits till there are no non-zero digits left and we emitted
     457              :      * at least one to the left of the decimal point.  digit_pos is the
     458              :      * current digit position, with zero as the digit just left of the decimal
     459              :      * point, increasing to the right.
     460              :      */
     461          265 :     digit_pos = points;
     462              :     do
     463              :     {
     464         2139 :         if (points && digit_pos == 0)
     465              :         {
     466              :             /* insert decimal point, but not if value cannot be fractional */
     467          265 :             *(--bufptr) = dsymbol;
     468              :         }
     469         1874 :         else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
     470              :         {
     471              :             /* insert thousands sep, but only to left of radix point */
     472          351 :             bufptr -= strlen(ssymbol);
     473          351 :             memcpy(bufptr, ssymbol, strlen(ssymbol));
     474              :         }
     475              : 
     476         2139 :         *(--bufptr) = (uvalue % 10) + '0';
     477         2139 :         uvalue = uvalue / 10;
     478         2139 :         digit_pos--;
     479         2139 :     } while (uvalue || digit_pos >= 0);
     480              : 
     481              :     /*----------
     482              :      * Now, attach currency symbol and sign symbol in the correct order.
     483              :      *
     484              :      * The POSIX spec defines these values controlling this code:
     485              :      *
     486              :      * p/n_sign_posn:
     487              :      *  0   Parentheses enclose the quantity and the currency_symbol.
     488              :      *  1   The sign string precedes the quantity and the currency_symbol.
     489              :      *  2   The sign string succeeds the quantity and the currency_symbol.
     490              :      *  3   The sign string precedes the currency_symbol.
     491              :      *  4   The sign string succeeds the currency_symbol.
     492              :      *
     493              :      * p/n_cs_precedes: 0 means currency symbol after value, else before it.
     494              :      *
     495              :      * p/n_sep_by_space:
     496              :      *  0   No <space> separates the currency symbol and value.
     497              :      *  1   If the currency symbol and sign string are adjacent, a <space>
     498              :      *      separates them from the value; otherwise, a <space> separates
     499              :      *      the currency symbol from the value.
     500              :      *  2   If the currency symbol and sign string are adjacent, a <space>
     501              :      *      separates them; otherwise, a <space> separates the sign string
     502              :      *      from the value.
     503              :      *----------
     504              :      */
     505          265 :     switch (sign_posn)
     506              :     {
     507            0 :         case 0:
     508            0 :             if (cs_precedes)
     509            0 :                 result = psprintf("(%s%s%s)",
     510              :                                   csymbol,
     511              :                                   (sep_by_space == 1) ? " " : "",
     512              :                                   bufptr);
     513              :             else
     514            0 :                 result = psprintf("(%s%s%s)",
     515              :                                   bufptr,
     516              :                                   (sep_by_space == 1) ? " " : "",
     517              :                                   csymbol);
     518            0 :             break;
     519          265 :         case 1:
     520              :         default:
     521          265 :             if (cs_precedes)
     522          265 :                 result = psprintf("%s%s%s%s%s",
     523              :                                   signsymbol,
     524              :                                   (sep_by_space == 2) ? " " : "",
     525              :                                   csymbol,
     526              :                                   (sep_by_space == 1) ? " " : "",
     527              :                                   bufptr);
     528              :             else
     529            0 :                 result = psprintf("%s%s%s%s%s",
     530              :                                   signsymbol,
     531              :                                   (sep_by_space == 2) ? " " : "",
     532              :                                   bufptr,
     533              :                                   (sep_by_space == 1) ? " " : "",
     534              :                                   csymbol);
     535          265 :             break;
     536            0 :         case 2:
     537            0 :             if (cs_precedes)
     538            0 :                 result = psprintf("%s%s%s%s%s",
     539              :                                   csymbol,
     540              :                                   (sep_by_space == 1) ? " " : "",
     541              :                                   bufptr,
     542              :                                   (sep_by_space == 2) ? " " : "",
     543              :                                   signsymbol);
     544              :             else
     545            0 :                 result = psprintf("%s%s%s%s%s",
     546              :                                   bufptr,
     547              :                                   (sep_by_space == 1) ? " " : "",
     548              :                                   csymbol,
     549              :                                   (sep_by_space == 2) ? " " : "",
     550              :                                   signsymbol);
     551            0 :             break;
     552            0 :         case 3:
     553            0 :             if (cs_precedes)
     554            0 :                 result = psprintf("%s%s%s%s%s",
     555              :                                   signsymbol,
     556              :                                   (sep_by_space == 2) ? " " : "",
     557              :                                   csymbol,
     558              :                                   (sep_by_space == 1) ? " " : "",
     559              :                                   bufptr);
     560              :             else
     561            0 :                 result = psprintf("%s%s%s%s%s",
     562              :                                   bufptr,
     563              :                                   (sep_by_space == 1) ? " " : "",
     564              :                                   signsymbol,
     565              :                                   (sep_by_space == 2) ? " " : "",
     566              :                                   csymbol);
     567            0 :             break;
     568            0 :         case 4:
     569            0 :             if (cs_precedes)
     570            0 :                 result = psprintf("%s%s%s%s%s",
     571              :                                   csymbol,
     572              :                                   (sep_by_space == 2) ? " " : "",
     573              :                                   signsymbol,
     574              :                                   (sep_by_space == 1) ? " " : "",
     575              :                                   bufptr);
     576              :             else
     577            0 :                 result = psprintf("%s%s%s%s%s",
     578              :                                   bufptr,
     579              :                                   (sep_by_space == 1) ? " " : "",
     580              :                                   csymbol,
     581              :                                   (sep_by_space == 2) ? " " : "",
     582              :                                   signsymbol);
     583            0 :             break;
     584              :     }
     585              : 
     586          265 :     PG_RETURN_CSTRING(result);
     587              : }
     588              : 
     589              : /*
     590              :  *      cash_recv           - converts external binary format to cash
     591              :  */
     592              : Datum
     593            0 : cash_recv(PG_FUNCTION_ARGS)
     594              : {
     595            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     596              : 
     597            0 :     PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
     598              : }
     599              : 
     600              : /*
     601              :  *      cash_send           - converts cash to binary format
     602              :  */
     603              : Datum
     604            0 : cash_send(PG_FUNCTION_ARGS)
     605              : {
     606            0 :     Cash        arg1 = PG_GETARG_CASH(0);
     607              :     StringInfoData buf;
     608              : 
     609            0 :     pq_begintypsend(&buf);
     610            0 :     pq_sendint64(&buf, arg1);
     611            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     612              : }
     613              : 
     614              : /*
     615              :  * Comparison functions
     616              :  */
     617              : 
     618              : Datum
     619          552 : cash_eq(PG_FUNCTION_ARGS)
     620              : {
     621          552 :     Cash        c1 = PG_GETARG_CASH(0);
     622          552 :     Cash        c2 = PG_GETARG_CASH(1);
     623              : 
     624          552 :     PG_RETURN_BOOL(c1 == c2);
     625              : }
     626              : 
     627              : Datum
     628            8 : cash_ne(PG_FUNCTION_ARGS)
     629              : {
     630            8 :     Cash        c1 = PG_GETARG_CASH(0);
     631            8 :     Cash        c2 = PG_GETARG_CASH(1);
     632              : 
     633            8 :     PG_RETURN_BOOL(c1 != c2);
     634              : }
     635              : 
     636              : Datum
     637          551 : cash_lt(PG_FUNCTION_ARGS)
     638              : {
     639          551 :     Cash        c1 = PG_GETARG_CASH(0);
     640          551 :     Cash        c2 = PG_GETARG_CASH(1);
     641              : 
     642          551 :     PG_RETURN_BOOL(c1 < c2);
     643              : }
     644              : 
     645              : Datum
     646          551 : cash_le(PG_FUNCTION_ARGS)
     647              : {
     648          551 :     Cash        c1 = PG_GETARG_CASH(0);
     649          551 :     Cash        c2 = PG_GETARG_CASH(1);
     650              : 
     651          551 :     PG_RETURN_BOOL(c1 <= c2);
     652              : }
     653              : 
     654              : Datum
     655          551 : cash_gt(PG_FUNCTION_ARGS)
     656              : {
     657          551 :     Cash        c1 = PG_GETARG_CASH(0);
     658          551 :     Cash        c2 = PG_GETARG_CASH(1);
     659              : 
     660          551 :     PG_RETURN_BOOL(c1 > c2);
     661              : }
     662              : 
     663              : Datum
     664          551 : cash_ge(PG_FUNCTION_ARGS)
     665              : {
     666          551 :     Cash        c1 = PG_GETARG_CASH(0);
     667          551 :     Cash        c2 = PG_GETARG_CASH(1);
     668              : 
     669          551 :     PG_RETURN_BOOL(c1 >= c2);
     670              : }
     671              : 
     672              : Datum
     673          663 : cash_cmp(PG_FUNCTION_ARGS)
     674              : {
     675          663 :     Cash        c1 = PG_GETARG_CASH(0);
     676          663 :     Cash        c2 = PG_GETARG_CASH(1);
     677              : 
     678          663 :     if (c1 > c2)
     679          561 :         PG_RETURN_INT32(1);
     680          102 :     else if (c1 == c2)
     681            7 :         PG_RETURN_INT32(0);
     682              :     else
     683           95 :         PG_RETURN_INT32(-1);
     684              : }
     685              : 
     686              : 
     687              : /* cash_pl()
     688              :  * Add two cash values.
     689              :  */
     690              : Datum
     691           20 : cash_pl(PG_FUNCTION_ARGS)
     692              : {
     693           20 :     Cash        c1 = PG_GETARG_CASH(0);
     694           20 :     Cash        c2 = PG_GETARG_CASH(1);
     695              : 
     696           20 :     PG_RETURN_CASH(cash_pl_cash(c1, c2));
     697              : }
     698              : 
     699              : 
     700              : /* cash_mi()
     701              :  * Subtract two cash values.
     702              :  */
     703              : Datum
     704           12 : cash_mi(PG_FUNCTION_ARGS)
     705              : {
     706           12 :     Cash        c1 = PG_GETARG_CASH(0);
     707           12 :     Cash        c2 = PG_GETARG_CASH(1);
     708              : 
     709           12 :     PG_RETURN_CASH(cash_mi_cash(c1, c2));
     710              : }
     711              : 
     712              : 
     713              : /* cash_div_cash()
     714              :  * Divide cash by cash, returning float8.
     715              :  */
     716              : Datum
     717            4 : cash_div_cash(PG_FUNCTION_ARGS)
     718              : {
     719            4 :     Cash        dividend = PG_GETARG_CASH(0);
     720            4 :     Cash        divisor = PG_GETARG_CASH(1);
     721              :     float8      quotient;
     722              : 
     723            4 :     if (divisor == 0)
     724            0 :         ereport(ERROR,
     725              :                 (errcode(ERRCODE_DIVISION_BY_ZERO),
     726              :                  errmsg("division by zero")));
     727              : 
     728            4 :     quotient = (float8) dividend / (float8) divisor;
     729            4 :     PG_RETURN_FLOAT8(quotient);
     730              : }
     731              : 
     732              : 
     733              : /* cash_mul_flt8()
     734              :  * Multiply cash by float8.
     735              :  */
     736              : Datum
     737           16 : cash_mul_flt8(PG_FUNCTION_ARGS)
     738              : {
     739           16 :     Cash        c = PG_GETARG_CASH(0);
     740           16 :     float8      f = PG_GETARG_FLOAT8(1);
     741              : 
     742           16 :     PG_RETURN_CASH(cash_mul_float8(c, f));
     743              : }
     744              : 
     745              : 
     746              : /* flt8_mul_cash()
     747              :  * Multiply float8 by cash.
     748              :  */
     749              : Datum
     750            4 : flt8_mul_cash(PG_FUNCTION_ARGS)
     751              : {
     752            4 :     float8      f = PG_GETARG_FLOAT8(0);
     753            4 :     Cash        c = PG_GETARG_CASH(1);
     754              : 
     755            4 :     PG_RETURN_CASH(cash_mul_float8(c, f));
     756              : }
     757              : 
     758              : 
     759              : /* cash_div_flt8()
     760              :  * Divide cash by float8.
     761              :  */
     762              : Datum
     763            9 : cash_div_flt8(PG_FUNCTION_ARGS)
     764              : {
     765            9 :     Cash        c = PG_GETARG_CASH(0);
     766            9 :     float8      f = PG_GETARG_FLOAT8(1);
     767              : 
     768            9 :     PG_RETURN_CASH(cash_div_float8(c, f));
     769              : }
     770              : 
     771              : 
     772              : /* cash_mul_flt4()
     773              :  * Multiply cash by float4.
     774              :  */
     775              : Datum
     776            8 : cash_mul_flt4(PG_FUNCTION_ARGS)
     777              : {
     778            8 :     Cash        c = PG_GETARG_CASH(0);
     779            8 :     float4      f = PG_GETARG_FLOAT4(1);
     780              : 
     781            8 :     PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
     782              : }
     783              : 
     784              : 
     785              : /* flt4_mul_cash()
     786              :  * Multiply float4 by cash.
     787              :  */
     788              : Datum
     789            4 : flt4_mul_cash(PG_FUNCTION_ARGS)
     790              : {
     791            4 :     float4      f = PG_GETARG_FLOAT4(0);
     792            4 :     Cash        c = PG_GETARG_CASH(1);
     793              : 
     794            4 :     PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
     795              : }
     796              : 
     797              : 
     798              : /* cash_div_flt4()
     799              :  * Divide cash by float4.
     800              :  *
     801              :  */
     802              : Datum
     803           13 : cash_div_flt4(PG_FUNCTION_ARGS)
     804              : {
     805           13 :     Cash        c = PG_GETARG_CASH(0);
     806           13 :     float4      f = PG_GETARG_FLOAT4(1);
     807              : 
     808           13 :     PG_RETURN_CASH(cash_div_float8(c, (float8) f));
     809              : }
     810              : 
     811              : 
     812              : /* cash_mul_int8()
     813              :  * Multiply cash by int8.
     814              :  */
     815              : Datum
     816            4 : cash_mul_int8(PG_FUNCTION_ARGS)
     817              : {
     818            4 :     Cash        c = PG_GETARG_CASH(0);
     819            4 :     int64       i = PG_GETARG_INT64(1);
     820              : 
     821            4 :     PG_RETURN_CASH(cash_mul_int64(c, i));
     822              : }
     823              : 
     824              : 
     825              : /* int8_mul_cash()
     826              :  * Multiply int8 by cash.
     827              :  */
     828              : Datum
     829            4 : int8_mul_cash(PG_FUNCTION_ARGS)
     830              : {
     831            4 :     int64       i = PG_GETARG_INT64(0);
     832            4 :     Cash        c = PG_GETARG_CASH(1);
     833              : 
     834            4 :     PG_RETURN_CASH(cash_mul_int64(c, i));
     835              : }
     836              : 
     837              : /* cash_div_int8()
     838              :  * Divide cash by 8-byte integer.
     839              :  */
     840              : Datum
     841           14 : cash_div_int8(PG_FUNCTION_ARGS)
     842              : {
     843           14 :     Cash        c = PG_GETARG_CASH(0);
     844           14 :     int64       i = PG_GETARG_INT64(1);
     845              : 
     846           14 :     PG_RETURN_CASH(cash_div_int64(c, i));
     847              : }
     848              : 
     849              : 
     850              : /* cash_mul_int4()
     851              :  * Multiply cash by int4.
     852              :  */
     853              : Datum
     854            8 : cash_mul_int4(PG_FUNCTION_ARGS)
     855              : {
     856            8 :     Cash        c = PG_GETARG_CASH(0);
     857            8 :     int32       i = PG_GETARG_INT32(1);
     858              : 
     859            8 :     PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
     860              : }
     861              : 
     862              : 
     863              : /* int4_mul_cash()
     864              :  * Multiply int4 by cash.
     865              :  */
     866              : Datum
     867            4 : int4_mul_cash(PG_FUNCTION_ARGS)
     868              : {
     869            4 :     int32       i = PG_GETARG_INT32(0);
     870            4 :     Cash        c = PG_GETARG_CASH(1);
     871              : 
     872            4 :     PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
     873              : }
     874              : 
     875              : 
     876              : /* cash_div_int4()
     877              :  * Divide cash by 4-byte integer.
     878              :  *
     879              :  */
     880              : Datum
     881           14 : cash_div_int4(PG_FUNCTION_ARGS)
     882              : {
     883           14 :     Cash        c = PG_GETARG_CASH(0);
     884           14 :     int32       i = PG_GETARG_INT32(1);
     885              : 
     886           14 :     PG_RETURN_CASH(cash_div_int64(c, (int64) i));
     887              : }
     888              : 
     889              : 
     890              : /* cash_mul_int2()
     891              :  * Multiply cash by int2.
     892              :  */
     893              : Datum
     894            4 : cash_mul_int2(PG_FUNCTION_ARGS)
     895              : {
     896            4 :     Cash        c = PG_GETARG_CASH(0);
     897            4 :     int16       s = PG_GETARG_INT16(1);
     898              : 
     899            4 :     PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
     900              : }
     901              : 
     902              : /* int2_mul_cash()
     903              :  * Multiply int2 by cash.
     904              :  */
     905              : Datum
     906            4 : int2_mul_cash(PG_FUNCTION_ARGS)
     907              : {
     908            4 :     int16       s = PG_GETARG_INT16(0);
     909            4 :     Cash        c = PG_GETARG_CASH(1);
     910              : 
     911            4 :     PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
     912              : }
     913              : 
     914              : /* cash_div_int2()
     915              :  * Divide cash by int2.
     916              :  *
     917              :  */
     918              : Datum
     919           18 : cash_div_int2(PG_FUNCTION_ARGS)
     920              : {
     921           18 :     Cash        c = PG_GETARG_CASH(0);
     922           18 :     int16       s = PG_GETARG_INT16(1);
     923              : 
     924           18 :     PG_RETURN_CASH(cash_div_int64(c, (int64) s));
     925              : }
     926              : 
     927              : /* cashlarger()
     928              :  * Return larger of two cash values.
     929              :  */
     930              : Datum
     931            4 : cashlarger(PG_FUNCTION_ARGS)
     932              : {
     933            4 :     Cash        c1 = PG_GETARG_CASH(0);
     934            4 :     Cash        c2 = PG_GETARG_CASH(1);
     935              :     Cash        result;
     936              : 
     937            4 :     result = (c1 > c2) ? c1 : c2;
     938              : 
     939            4 :     PG_RETURN_CASH(result);
     940              : }
     941              : 
     942              : /* cashsmaller()
     943              :  * Return smaller of two cash values.
     944              :  */
     945              : Datum
     946            4 : cashsmaller(PG_FUNCTION_ARGS)
     947              : {
     948            4 :     Cash        c1 = PG_GETARG_CASH(0);
     949            4 :     Cash        c2 = PG_GETARG_CASH(1);
     950              :     Cash        result;
     951              : 
     952            4 :     result = (c1 < c2) ? c1 : c2;
     953              : 
     954            4 :     PG_RETURN_CASH(result);
     955              : }
     956              : 
     957              : /* cash_words()
     958              :  * This converts an int4 as well but to a representation using words
     959              :  * Obviously way North American centric - sorry
     960              :  */
     961              : Datum
     962            8 : cash_words(PG_FUNCTION_ARGS)
     963              : {
     964            8 :     Cash        value = PG_GETARG_CASH(0);
     965              :     uint64      val;
     966              :     StringInfoData buf;
     967              :     text       *res;
     968              :     Cash        dollars;
     969              :     Cash        m0;
     970              :     Cash        m1;
     971              :     Cash        m2;
     972              :     Cash        m3;
     973              :     Cash        m4;
     974              :     Cash        m5;
     975              :     Cash        m6;
     976              : 
     977            8 :     initStringInfo(&buf);
     978              : 
     979              :     /* work with positive numbers */
     980            8 :     if (value < 0)
     981              :     {
     982            0 :         value = -value;
     983            0 :         appendStringInfoString(&buf, "minus ");
     984              :     }
     985              : 
     986              :     /* Now treat as unsigned, to avoid trouble at INT_MIN */
     987            8 :     val = (uint64) value;
     988              : 
     989            8 :     dollars = val / INT64CONST(100);
     990            8 :     m0 = val % INT64CONST(100); /* cents */
     991            8 :     m1 = (val / INT64CONST(100)) % 1000;    /* hundreds */
     992            8 :     m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
     993            8 :     m3 = (val / INT64CONST(100000000)) % 1000;  /* millions */
     994            8 :     m4 = (val / INT64CONST(100000000000)) % 1000;   /* billions */
     995            8 :     m5 = (val / INT64CONST(100000000000000)) % 1000;    /* trillions */
     996            8 :     m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
     997              : 
     998            8 :     if (m6)
     999              :     {
    1000            0 :         append_num_word(&buf, m6);
    1001            0 :         appendStringInfoString(&buf, " quadrillion ");
    1002              :     }
    1003              : 
    1004            8 :     if (m5)
    1005              :     {
    1006            0 :         append_num_word(&buf, m5);
    1007            0 :         appendStringInfoString(&buf, " trillion ");
    1008              :     }
    1009              : 
    1010            8 :     if (m4)
    1011              :     {
    1012            0 :         append_num_word(&buf, m4);
    1013            0 :         appendStringInfoString(&buf, " billion ");
    1014              :     }
    1015              : 
    1016            8 :     if (m3)
    1017              :     {
    1018            0 :         append_num_word(&buf, m3);
    1019            0 :         appendStringInfoString(&buf, " million ");
    1020              :     }
    1021              : 
    1022            8 :     if (m2)
    1023              :     {
    1024            0 :         append_num_word(&buf, m2);
    1025            0 :         appendStringInfoString(&buf, " thousand ");
    1026              :     }
    1027              : 
    1028            8 :     if (m1)
    1029            8 :         append_num_word(&buf, m1);
    1030              : 
    1031            8 :     if (dollars == 0)
    1032            0 :         appendStringInfoString(&buf, "zero");
    1033              : 
    1034            8 :     appendStringInfoString(&buf, dollars == 1 ? " dollar and " : " dollars and ");
    1035            8 :     append_num_word(&buf, m0);
    1036            8 :     appendStringInfoString(&buf, m0 == 1 ? " cent" : " cents");
    1037              : 
    1038              :     /* capitalize output */
    1039            8 :     buf.data[0] = pg_ascii_toupper((unsigned char) buf.data[0]);
    1040              : 
    1041              :     /* return as text datum */
    1042            8 :     res = cstring_to_text_with_len(buf.data, buf.len);
    1043            8 :     pfree(buf.data);
    1044            8 :     PG_RETURN_TEXT_P(res);
    1045              : }
    1046              : 
    1047              : 
    1048              : /* cash_numeric()
    1049              :  * Convert cash to numeric.
    1050              :  */
    1051              : Datum
    1052           16 : cash_numeric(PG_FUNCTION_ARGS)
    1053              : {
    1054           16 :     Cash        money = PG_GETARG_CASH(0);
    1055              :     Datum       result;
    1056              :     int         fpoint;
    1057           16 :     struct lconv *lconvert = PGLC_localeconv();
    1058              : 
    1059              :     /* see comments about frac_digits in cash_in() */
    1060           16 :     fpoint = lconvert->frac_digits;
    1061           16 :     if (fpoint < 0 || fpoint > 10)
    1062           16 :         fpoint = 2;
    1063              : 
    1064              :     /* convert the integral money value to numeric */
    1065           16 :     result = NumericGetDatum(int64_to_numeric(money));
    1066              : 
    1067              :     /* scale appropriately, if needed */
    1068           16 :     if (fpoint > 0)
    1069              :     {
    1070              :         int64       scale;
    1071              :         int         i;
    1072              :         Datum       numeric_scale;
    1073              :         Datum       quotient;
    1074              : 
    1075              :         /* compute required scale factor */
    1076           16 :         scale = 1;
    1077           48 :         for (i = 0; i < fpoint; i++)
    1078           32 :             scale *= 10;
    1079           16 :         numeric_scale = NumericGetDatum(int64_to_numeric(scale));
    1080              : 
    1081              :         /*
    1082              :          * Given integral inputs approaching INT64_MAX, select_div_scale()
    1083              :          * might choose a result scale of zero, causing loss of fractional
    1084              :          * digits in the quotient.  We can ensure an exact result by setting
    1085              :          * the dscale of either input to be at least as large as the desired
    1086              :          * result scale.  numeric_round() will do that for us.
    1087              :          */
    1088           16 :         numeric_scale = DirectFunctionCall2(numeric_round,
    1089              :                                             numeric_scale,
    1090              :                                             Int32GetDatum(fpoint));
    1091              : 
    1092              :         /* Now we can safely divide ... */
    1093           16 :         quotient = DirectFunctionCall2(numeric_div, result, numeric_scale);
    1094              : 
    1095              :         /* ... and forcibly round to exactly the intended number of digits */
    1096           16 :         result = DirectFunctionCall2(numeric_round,
    1097              :                                      quotient,
    1098              :                                      Int32GetDatum(fpoint));
    1099              :     }
    1100              : 
    1101           16 :     PG_RETURN_DATUM(result);
    1102              : }
    1103              : 
    1104              : /* numeric_cash()
    1105              :  * Convert numeric to cash.
    1106              :  */
    1107              : Datum
    1108            8 : numeric_cash(PG_FUNCTION_ARGS)
    1109              : {
    1110            8 :     Numeric     amount = PG_GETARG_NUMERIC(0);
    1111              :     Cash        result;
    1112              :     int         fpoint;
    1113              :     int64       scale;
    1114              :     int         i;
    1115              :     Numeric     numeric_scale;
    1116            8 :     struct lconv *lconvert = PGLC_localeconv();
    1117              : 
    1118              :     /* see comments about frac_digits in cash_in() */
    1119            8 :     fpoint = lconvert->frac_digits;
    1120            8 :     if (fpoint < 0 || fpoint > 10)
    1121            8 :         fpoint = 2;
    1122              : 
    1123              :     /* compute required scale factor */
    1124            8 :     scale = 1;
    1125           24 :     for (i = 0; i < fpoint; i++)
    1126           16 :         scale *= 10;
    1127              : 
    1128              :     /* multiply the input amount by scale factor */
    1129            8 :     numeric_scale = int64_to_numeric(scale);
    1130              : 
    1131            8 :     amount = numeric_mul_safe(amount, numeric_scale, fcinfo->context);
    1132            8 :     if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
    1133            0 :         PG_RETURN_NULL();
    1134              : 
    1135              :     /* note that numeric_int8 will round to nearest integer for us */
    1136            8 :     result = numeric_int8_safe(amount, fcinfo->context);
    1137            8 :     if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
    1138            0 :         PG_RETURN_NULL();
    1139              : 
    1140            8 :     PG_RETURN_CASH(result);
    1141              : }
    1142              : 
    1143              : /* int4_cash()
    1144              :  * Convert int4 (int) to cash
    1145              :  */
    1146              : Datum
    1147           28 : int4_cash(PG_FUNCTION_ARGS)
    1148              : {
    1149           28 :     int32       amount = PG_GETARG_INT32(0);
    1150              :     Cash        result;
    1151              :     int         fpoint;
    1152              :     int64       scale;
    1153              :     int         i;
    1154           28 :     struct lconv *lconvert = PGLC_localeconv();
    1155              : 
    1156              :     /* see comments about frac_digits in cash_in() */
    1157           28 :     fpoint = lconvert->frac_digits;
    1158           28 :     if (fpoint < 0 || fpoint > 10)
    1159           28 :         fpoint = 2;
    1160              : 
    1161              :     /* compute required scale factor */
    1162           28 :     scale = 1;
    1163           84 :     for (i = 0; i < fpoint; i++)
    1164           56 :         scale *= 10;
    1165              : 
    1166              :     /* compute amount * scale, checking for overflow */
    1167           28 :     if (unlikely(pg_mul_s64_overflow(amount, scale, &result)))
    1168            0 :         ereturn(fcinfo->context, (Datum) 0,
    1169              :                 errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
    1170              :                 errmsg("bigint out of range"));
    1171              : 
    1172           28 :     PG_RETURN_CASH(result);
    1173              : }
    1174              : 
    1175              : /* int8_cash()
    1176              :  * Convert int8 (bigint) to cash
    1177              :  */
    1178              : Datum
    1179           16 : int8_cash(PG_FUNCTION_ARGS)
    1180              : {
    1181           16 :     int64       amount = PG_GETARG_INT64(0);
    1182              :     Cash        result;
    1183              :     int         fpoint;
    1184              :     int64       scale;
    1185              :     int         i;
    1186           16 :     struct lconv *lconvert = PGLC_localeconv();
    1187              : 
    1188              :     /* see comments about frac_digits in cash_in() */
    1189           16 :     fpoint = lconvert->frac_digits;
    1190           16 :     if (fpoint < 0 || fpoint > 10)
    1191           16 :         fpoint = 2;
    1192              : 
    1193              :     /* compute required scale factor */
    1194           16 :     scale = 1;
    1195           48 :     for (i = 0; i < fpoint; i++)
    1196           32 :         scale *= 10;
    1197              : 
    1198              :     /* compute amount * scale, checking for overflow */
    1199           16 :     if (unlikely(pg_mul_s64_overflow(amount, scale, &result)))
    1200            0 :         ereturn(fcinfo->context, (Datum) 0,
    1201              :                 errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
    1202              :                 errmsg("bigint out of range"));
    1203              : 
    1204           16 :     PG_RETURN_CASH(result);
    1205              : }
        

Generated by: LCOV version 2.0-1