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

Generated by: LCOV version 1.14