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

Generated by: LCOV version 2.0-1