LCOV - code coverage report
Current view: top level - src/backend/utils/adt - cash.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 84.0 % 413 347
Test Date: 2026-03-01 04:14:54 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 "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           12 : 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           12 :     const char *const *big = small + 18;
      48           12 :     int         tu = value % 100;
      49              : 
      50              :     /* deal with the simple cases first */
      51           12 :     if (value <= 20)
      52              :     {
      53            3 :         appendStringInfoString(buf, small[value]);
      54            3 :         return;
      55              :     }
      56              : 
      57              :     /* is it an even multiple of 100? */
      58            9 :     if (!tu)
      59              :     {
      60            0 :         appendStringInfo(buf, "%s hundred", small[value / 100]);
      61            0 :         return;
      62              :     }
      63              : 
      64              :     /* more than 99? */
      65            9 :     if (value > 99)
      66              :     {
      67              :         /* is it an even multiple of 10 other than 10? */
      68            6 :         if (value % 10 == 0 && tu > 10)
      69            0 :             appendStringInfo(buf, "%s hundred %s",
      70            0 :                              small[value / 100], big[tu / 10]);
      71            6 :         else if (tu < 20)
      72            0 :             appendStringInfo(buf, "%s hundred and %s",
      73            0 :                              small[value / 100], small[tu]);
      74              :         else
      75            6 :             appendStringInfo(buf, "%s hundred %s %s",
      76            6 :                              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            3 :         if (value % 10 == 0 && tu > 10)
      82            0 :             appendStringInfoString(buf, big[tu / 10]);
      83            3 :         else if (tu < 20)
      84            0 :             appendStringInfoString(buf, small[tu]);
      85              :         else
      86            3 :             appendStringInfo(buf, "%s %s", big[tu / 10], small[tu % 10]);
      87              :     }
      88              : }
      89              : 
      90              : static inline Cash
      91           15 : cash_pl_cash(Cash c1, Cash c2)
      92              : {
      93              :     Cash        res;
      94              : 
      95           15 :     if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
      96            3 :         ereport(ERROR,
      97              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
      98              :                  errmsg("money out of range")));
      99              : 
     100           12 :     return res;
     101              : }
     102              : 
     103              : static inline Cash
     104            9 : cash_mi_cash(Cash c1, Cash c2)
     105              : {
     106              :     Cash        res;
     107              : 
     108            9 :     if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
     109            3 :         ereport(ERROR,
     110              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     111              :                  errmsg("money out of range")));
     112              : 
     113            6 :     return res;
     114              : }
     115              : 
     116              : static inline Cash
     117           24 : cash_mul_float8(Cash c, float8 f)
     118              : {
     119           24 :     float8      res = rint(float8_mul((float8) c, f));
     120              : 
     121           24 :     if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
     122           12 :         ereport(ERROR,
     123              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     124              :                  errmsg("money out of range")));
     125              : 
     126           12 :     return (Cash) res;
     127              : }
     128              : 
     129              : static inline Cash
     130           15 : cash_div_float8(Cash c, float8 f)
     131              : {
     132           15 :     float8      res = rint(float8_div((float8) c, f));
     133              : 
     134           15 :     if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
     135            3 :         ereport(ERROR,
     136              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     137              :                  errmsg("money out of range")));
     138              : 
     139           12 :     return (Cash) res;
     140              : }
     141              : 
     142              : static inline Cash
     143           21 : cash_mul_int64(Cash c, int64 i)
     144              : {
     145              :     Cash        res;
     146              : 
     147           21 :     if (unlikely(pg_mul_s64_overflow(c, i, &res)))
     148            3 :         ereport(ERROR,
     149              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     150              :                  errmsg("money out of range")));
     151              : 
     152           18 :     return res;
     153              : }
     154              : 
     155              : static inline Cash
     156           30 : cash_div_int64(Cash c, int64 i)
     157              : {
     158           30 :     if (unlikely(i == 0))
     159            3 :         ereport(ERROR,
     160              :                 (errcode(ERRCODE_DIVISION_BY_ZERO),
     161              :                  errmsg("division by zero")));
     162              : 
     163           27 :     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          803 : cash_in(PG_FUNCTION_ARGS)
     174              : {
     175          803 :     char       *str = PG_GETARG_CSTRING(0);
     176          803 :     Node       *escontext = fcinfo->context;
     177              :     Cash        result;
     178          803 :     Cash        value = 0;
     179          803 :     Cash        dec = 0;
     180          803 :     Cash        sgn = 1;
     181          803 :     bool        seen_dot = false;
     182          803 :     const char *s = str;
     183              :     int         fpoint;
     184              :     char        dsymbol;
     185              :     const char *ssymbol,
     186              :                *psymbol,
     187              :                *nsymbol,
     188              :                *csymbol;
     189          803 :     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          803 :     fpoint = lconvert->frac_digits;
     202          803 :     if (fpoint < 0 || fpoint > 10)
     203          803 :         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          803 :     if (*lconvert->mon_decimal_point != '\0' &&
     207            0 :         lconvert->mon_decimal_point[1] == '\0')
     208            0 :         dsymbol = *lconvert->mon_decimal_point;
     209              :     else
     210          803 :         dsymbol = '.';
     211          803 :     if (*lconvert->mon_thousands_sep != '\0')
     212            0 :         ssymbol = lconvert->mon_thousands_sep;
     213              :     else                        /* ssymbol should not equal dsymbol */
     214          803 :         ssymbol = (dsymbol != ',') ? "," : ".";
     215          803 :     csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
     216          803 :     psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
     217          803 :     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          803 :     while (isspace((unsigned char) *s))
     227            0 :         s++;
     228          803 :     if (strncmp(s, csymbol, strlen(csymbol)) == 0)
     229           60 :         s += strlen(csymbol);
     230          803 :     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          803 :     if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
     241              :     {
     242          311 :         sgn = -1;
     243          311 :         s += strlen(nsymbol);
     244              :     }
     245          492 :     else if (*s == '(')
     246              :     {
     247            6 :         sgn = -1;
     248            6 :         s++;
     249              :     }
     250          486 :     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          803 :     while (isspace((unsigned char) *s))
     259            0 :         s++;
     260          803 :     if (strncmp(s, csymbol, strlen(csymbol)) == 0)
     261            3 :         s += strlen(csymbol);
     262          803 :     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         7913 :     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         7152 :         if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
     285         6389 :         {
     286         6398 :             int8        digit = *s - '0';
     287              : 
     288        12790 :             if (pg_mul_s64_overflow(value, 10, &value) ||
     289         6392 :                 pg_sub_s64_overflow(value, digit, &value))
     290            9 :                 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         6389 :             if (seen_dot)
     296         1427 :                 dec++;
     297              :         }
     298              :         /* decimal point? then start counting fractions... */
     299          754 :         else if (*s == dsymbol && !seen_dot)
     300              :         {
     301          718 :             seen_dot = true;
     302              :         }
     303              :         /* ignore if "thousands" separator, else we're done */
     304           36 :         else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
     305            3 :             s += strlen(ssymbol) - 1;
     306              :         else
     307           33 :             break;
     308              :     }
     309              : 
     310              :     /* round off if there's another digit */
     311          794 :     if (isdigit((unsigned char) *s) && *s >= '5')
     312              :     {
     313              :         /* remember we build the value in the negative */
     314           15 :         if (pg_sub_s64_overflow(value, 1, &value))
     315            3 :             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          943 :     for (; dec < fpoint; dec++)
     323              :     {
     324          164 :         if (pg_mul_s64_overflow(value, 10, &value))
     325           12 :             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          797 :     while (isdigit((unsigned char) *s))
     336           18 :         s++;
     337              : 
     338          785 :     while (*s)
     339              :     {
     340           12 :         if (isspace((unsigned char) *s) || *s == ')')
     341            6 :             s++;
     342            6 :         else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
     343              :         {
     344            0 :             sgn = -1;
     345            0 :             s += strlen(nsymbol);
     346              :         }
     347            6 :         else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
     348            0 :             s += strlen(psymbol);
     349            6 :         else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
     350            0 :             s += strlen(csymbol);
     351              :         else
     352            6 :             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          773 :     if (sgn > 0)
     363              :     {
     364          468 :         if (value == PG_INT64_MIN)
     365            6 :             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          462 :         result = -value;
     370              :     }
     371              :     else
     372          305 :         result = value;
     373              : 
     374              : #ifdef CASHDEBUG
     375              :     printf("cashin- result is " INT64_FORMAT "\n", result);
     376              : #endif
     377              : 
     378          767 :     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          208 : cash_out(PG_FUNCTION_ARGS)
     388              : {
     389          208 :     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          208 :     struct lconv *lconvert = PGLC_localeconv();
     405              : 
     406              :     /* see comments about frac_digits in cash_in() */
     407          208 :     points = lconvert->frac_digits;
     408          208 :     if (points < 0 || points > 10)
     409          208 :         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          208 :     mon_group = *lconvert->mon_grouping;
     416          208 :     if (mon_group <= 0 || mon_group > 6)
     417          208 :         mon_group = 3;
     418              : 
     419              :     /* we restrict dsymbol to be a single byte, but not the other symbols */
     420          208 :     if (*lconvert->mon_decimal_point != '\0' &&
     421            0 :         lconvert->mon_decimal_point[1] == '\0')
     422            0 :         dsymbol = *lconvert->mon_decimal_point;
     423              :     else
     424          208 :         dsymbol = '.';
     425          208 :     if (*lconvert->mon_thousands_sep != '\0')
     426            0 :         ssymbol = lconvert->mon_thousands_sep;
     427              :     else                        /* ssymbol should not equal dsymbol */
     428          208 :         ssymbol = (dsymbol != ',') ? "," : ".";
     429          208 :     csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
     430              : 
     431          208 :     if (value < 0)
     432              :     {
     433              :         /* set up formatting data */
     434           43 :         signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
     435           43 :         sign_posn = lconvert->n_sign_posn;
     436           43 :         cs_precedes = lconvert->n_cs_precedes;
     437           43 :         sep_by_space = lconvert->n_sep_by_space;
     438              :     }
     439              :     else
     440              :     {
     441          165 :         signsymbol = lconvert->positive_sign;
     442          165 :         sign_posn = lconvert->p_sign_posn;
     443          165 :         cs_precedes = lconvert->p_cs_precedes;
     444          165 :         sep_by_space = lconvert->p_sep_by_space;
     445              :     }
     446              : 
     447              :     /* make the amount positive for digit-reconstruction loop */
     448          208 :     uvalue = pg_abs_s64(value);
     449              : 
     450              :     /* we build the digits+decimal-point+sep string right-to-left in buf[] */
     451          208 :     bufptr = buf + sizeof(buf) - 1;
     452          208 :     *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          208 :     digit_pos = points;
     461              :     do
     462              :     {
     463         1644 :         if (points && digit_pos == 0)
     464              :         {
     465              :             /* insert decimal point, but not if value cannot be fractional */
     466          208 :             *(--bufptr) = dsymbol;
     467              :         }
     468         1436 :         else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
     469              :         {
     470              :             /* insert thousands sep, but only to left of radix point */
     471          265 :             bufptr -= strlen(ssymbol);
     472          265 :             memcpy(bufptr, ssymbol, strlen(ssymbol));
     473              :         }
     474              : 
     475         1644 :         *(--bufptr) = (uvalue % 10) + '0';
     476         1644 :         uvalue = uvalue / 10;
     477         1644 :         digit_pos--;
     478         1644 :     } 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          208 :     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          208 :         case 1:
     519              :         default:
     520          208 :             if (cs_precedes)
     521          208 :                 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          208 :             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          208 :     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          550 : cash_eq(PG_FUNCTION_ARGS)
     619              : {
     620          550 :     Cash        c1 = PG_GETARG_CASH(0);
     621          550 :     Cash        c2 = PG_GETARG_CASH(1);
     622              : 
     623          550 :     PG_RETURN_BOOL(c1 == c2);
     624              : }
     625              : 
     626              : Datum
     627            6 : cash_ne(PG_FUNCTION_ARGS)
     628              : {
     629            6 :     Cash        c1 = PG_GETARG_CASH(0);
     630            6 :     Cash        c2 = PG_GETARG_CASH(1);
     631              : 
     632            6 :     PG_RETURN_BOOL(c1 != c2);
     633              : }
     634              : 
     635              : Datum
     636          549 : cash_lt(PG_FUNCTION_ARGS)
     637              : {
     638          549 :     Cash        c1 = PG_GETARG_CASH(0);
     639          549 :     Cash        c2 = PG_GETARG_CASH(1);
     640              : 
     641          549 :     PG_RETURN_BOOL(c1 < c2);
     642              : }
     643              : 
     644              : Datum
     645          549 : cash_le(PG_FUNCTION_ARGS)
     646              : {
     647          549 :     Cash        c1 = PG_GETARG_CASH(0);
     648          549 :     Cash        c2 = PG_GETARG_CASH(1);
     649              : 
     650          549 :     PG_RETURN_BOOL(c1 <= c2);
     651              : }
     652              : 
     653              : Datum
     654          549 : cash_gt(PG_FUNCTION_ARGS)
     655              : {
     656          549 :     Cash        c1 = PG_GETARG_CASH(0);
     657          549 :     Cash        c2 = PG_GETARG_CASH(1);
     658              : 
     659          549 :     PG_RETURN_BOOL(c1 > c2);
     660              : }
     661              : 
     662              : Datum
     663          549 : cash_ge(PG_FUNCTION_ARGS)
     664              : {
     665          549 :     Cash        c1 = PG_GETARG_CASH(0);
     666          549 :     Cash        c2 = PG_GETARG_CASH(1);
     667              : 
     668          549 :     PG_RETURN_BOOL(c1 >= c2);
     669              : }
     670              : 
     671              : Datum
     672          659 : cash_cmp(PG_FUNCTION_ARGS)
     673              : {
     674          659 :     Cash        c1 = PG_GETARG_CASH(0);
     675          659 :     Cash        c2 = PG_GETARG_CASH(1);
     676              : 
     677          659 :     if (c1 > c2)
     678          561 :         PG_RETURN_INT32(1);
     679           98 :     else if (c1 == c2)
     680            7 :         PG_RETURN_INT32(0);
     681              :     else
     682           91 :         PG_RETURN_INT32(-1);
     683              : }
     684              : 
     685              : 
     686              : /* cash_pl()
     687              :  * Add two cash values.
     688              :  */
     689              : Datum
     690           15 : cash_pl(PG_FUNCTION_ARGS)
     691              : {
     692           15 :     Cash        c1 = PG_GETARG_CASH(0);
     693           15 :     Cash        c2 = PG_GETARG_CASH(1);
     694              : 
     695           15 :     PG_RETURN_CASH(cash_pl_cash(c1, c2));
     696              : }
     697              : 
     698              : 
     699              : /* cash_mi()
     700              :  * Subtract two cash values.
     701              :  */
     702              : Datum
     703            9 : cash_mi(PG_FUNCTION_ARGS)
     704              : {
     705            9 :     Cash        c1 = PG_GETARG_CASH(0);
     706            9 :     Cash        c2 = PG_GETARG_CASH(1);
     707              : 
     708            9 :     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            3 : cash_div_cash(PG_FUNCTION_ARGS)
     717              : {
     718            3 :     Cash        dividend = PG_GETARG_CASH(0);
     719            3 :     Cash        divisor = PG_GETARG_CASH(1);
     720              :     float8      quotient;
     721              : 
     722            3 :     if (divisor == 0)
     723            0 :         ereport(ERROR,
     724              :                 (errcode(ERRCODE_DIVISION_BY_ZERO),
     725              :                  errmsg("division by zero")));
     726              : 
     727            3 :     quotient = (float8) dividend / (float8) divisor;
     728            3 :     PG_RETURN_FLOAT8(quotient);
     729              : }
     730              : 
     731              : 
     732              : /* cash_mul_flt8()
     733              :  * Multiply cash by float8.
     734              :  */
     735              : Datum
     736           12 : cash_mul_flt8(PG_FUNCTION_ARGS)
     737              : {
     738           12 :     Cash        c = PG_GETARG_CASH(0);
     739           12 :     float8      f = PG_GETARG_FLOAT8(1);
     740              : 
     741           12 :     PG_RETURN_CASH(cash_mul_float8(c, f));
     742              : }
     743              : 
     744              : 
     745              : /* flt8_mul_cash()
     746              :  * Multiply float8 by cash.
     747              :  */
     748              : Datum
     749            3 : flt8_mul_cash(PG_FUNCTION_ARGS)
     750              : {
     751            3 :     float8      f = PG_GETARG_FLOAT8(0);
     752            3 :     Cash        c = PG_GETARG_CASH(1);
     753              : 
     754            3 :     PG_RETURN_CASH(cash_mul_float8(c, f));
     755              : }
     756              : 
     757              : 
     758              : /* cash_div_flt8()
     759              :  * Divide cash by float8.
     760              :  */
     761              : Datum
     762            6 : cash_div_flt8(PG_FUNCTION_ARGS)
     763              : {
     764            6 :     Cash        c = PG_GETARG_CASH(0);
     765            6 :     float8      f = PG_GETARG_FLOAT8(1);
     766              : 
     767            6 :     PG_RETURN_CASH(cash_div_float8(c, f));
     768              : }
     769              : 
     770              : 
     771              : /* cash_mul_flt4()
     772              :  * Multiply cash by float4.
     773              :  */
     774              : Datum
     775            6 : cash_mul_flt4(PG_FUNCTION_ARGS)
     776              : {
     777            6 :     Cash        c = PG_GETARG_CASH(0);
     778            6 :     float4      f = PG_GETARG_FLOAT4(1);
     779              : 
     780            6 :     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            3 : flt4_mul_cash(PG_FUNCTION_ARGS)
     789              : {
     790            3 :     float4      f = PG_GETARG_FLOAT4(0);
     791            3 :     Cash        c = PG_GETARG_CASH(1);
     792              : 
     793            3 :     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            9 : cash_div_flt4(PG_FUNCTION_ARGS)
     803              : {
     804            9 :     Cash        c = PG_GETARG_CASH(0);
     805            9 :     float4      f = PG_GETARG_FLOAT4(1);
     806              : 
     807            9 :     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            3 : cash_mul_int8(PG_FUNCTION_ARGS)
     816              : {
     817            3 :     Cash        c = PG_GETARG_CASH(0);
     818            3 :     int64       i = PG_GETARG_INT64(1);
     819              : 
     820            3 :     PG_RETURN_CASH(cash_mul_int64(c, i));
     821              : }
     822              : 
     823              : 
     824              : /* int8_mul_cash()
     825              :  * Multiply int8 by cash.
     826              :  */
     827              : Datum
     828            3 : int8_mul_cash(PG_FUNCTION_ARGS)
     829              : {
     830            3 :     int64       i = PG_GETARG_INT64(0);
     831            3 :     Cash        c = PG_GETARG_CASH(1);
     832              : 
     833            3 :     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            9 : cash_div_int8(PG_FUNCTION_ARGS)
     841              : {
     842            9 :     Cash        c = PG_GETARG_CASH(0);
     843            9 :     int64       i = PG_GETARG_INT64(1);
     844              : 
     845            9 :     PG_RETURN_CASH(cash_div_int64(c, i));
     846              : }
     847              : 
     848              : 
     849              : /* cash_mul_int4()
     850              :  * Multiply cash by int4.
     851              :  */
     852              : Datum
     853            6 : cash_mul_int4(PG_FUNCTION_ARGS)
     854              : {
     855            6 :     Cash        c = PG_GETARG_CASH(0);
     856            6 :     int32       i = PG_GETARG_INT32(1);
     857              : 
     858            6 :     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            3 : int4_mul_cash(PG_FUNCTION_ARGS)
     867              : {
     868            3 :     int32       i = PG_GETARG_INT32(0);
     869            3 :     Cash        c = PG_GETARG_CASH(1);
     870              : 
     871            3 :     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            9 : cash_div_int4(PG_FUNCTION_ARGS)
     881              : {
     882            9 :     Cash        c = PG_GETARG_CASH(0);
     883            9 :     int32       i = PG_GETARG_INT32(1);
     884              : 
     885            9 :     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            3 : cash_mul_int2(PG_FUNCTION_ARGS)
     894              : {
     895            3 :     Cash        c = PG_GETARG_CASH(0);
     896            3 :     int16       s = PG_GETARG_INT16(1);
     897              : 
     898            3 :     PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
     899              : }
     900              : 
     901              : /* int2_mul_cash()
     902              :  * Multiply int2 by cash.
     903              :  */
     904              : Datum
     905            3 : int2_mul_cash(PG_FUNCTION_ARGS)
     906              : {
     907            3 :     int16       s = PG_GETARG_INT16(0);
     908            3 :     Cash        c = PG_GETARG_CASH(1);
     909              : 
     910            3 :     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           12 : cash_div_int2(PG_FUNCTION_ARGS)
     919              : {
     920           12 :     Cash        c = PG_GETARG_CASH(0);
     921           12 :     int16       s = PG_GETARG_INT16(1);
     922              : 
     923           12 :     PG_RETURN_CASH(cash_div_int64(c, (int64) s));
     924              : }
     925              : 
     926              : /* cashlarger()
     927              :  * Return larger of two cash values.
     928              :  */
     929              : Datum
     930            3 : cashlarger(PG_FUNCTION_ARGS)
     931              : {
     932            3 :     Cash        c1 = PG_GETARG_CASH(0);
     933            3 :     Cash        c2 = PG_GETARG_CASH(1);
     934              :     Cash        result;
     935              : 
     936            3 :     result = (c1 > c2) ? c1 : c2;
     937              : 
     938            3 :     PG_RETURN_CASH(result);
     939              : }
     940              : 
     941              : /* cashsmaller()
     942              :  * Return smaller of two cash values.
     943              :  */
     944              : Datum
     945            3 : cashsmaller(PG_FUNCTION_ARGS)
     946              : {
     947            3 :     Cash        c1 = PG_GETARG_CASH(0);
     948            3 :     Cash        c2 = PG_GETARG_CASH(1);
     949              :     Cash        result;
     950              : 
     951            3 :     result = (c1 < c2) ? c1 : c2;
     952              : 
     953            3 :     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            6 : cash_words(PG_FUNCTION_ARGS)
     962              : {
     963            6 :     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            6 :     initStringInfo(&buf);
     977              : 
     978              :     /* work with positive numbers */
     979            6 :     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            6 :     val = (uint64) value;
     987              : 
     988            6 :     dollars = val / INT64CONST(100);
     989            6 :     m0 = val % INT64CONST(100); /* cents */
     990            6 :     m1 = (val / INT64CONST(100)) % 1000;    /* hundreds */
     991            6 :     m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
     992            6 :     m3 = (val / INT64CONST(100000000)) % 1000;  /* millions */
     993            6 :     m4 = (val / INT64CONST(100000000000)) % 1000;   /* billions */
     994            6 :     m5 = (val / INT64CONST(100000000000000)) % 1000;    /* trillions */
     995            6 :     m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
     996              : 
     997            6 :     if (m6)
     998              :     {
     999            0 :         append_num_word(&buf, m6);
    1000            0 :         appendStringInfoString(&buf, " quadrillion ");
    1001              :     }
    1002              : 
    1003            6 :     if (m5)
    1004              :     {
    1005            0 :         append_num_word(&buf, m5);
    1006            0 :         appendStringInfoString(&buf, " trillion ");
    1007              :     }
    1008              : 
    1009            6 :     if (m4)
    1010              :     {
    1011            0 :         append_num_word(&buf, m4);
    1012            0 :         appendStringInfoString(&buf, " billion ");
    1013              :     }
    1014              : 
    1015            6 :     if (m3)
    1016              :     {
    1017            0 :         append_num_word(&buf, m3);
    1018            0 :         appendStringInfoString(&buf, " million ");
    1019              :     }
    1020              : 
    1021            6 :     if (m2)
    1022              :     {
    1023            0 :         append_num_word(&buf, m2);
    1024            0 :         appendStringInfoString(&buf, " thousand ");
    1025              :     }
    1026              : 
    1027            6 :     if (m1)
    1028            6 :         append_num_word(&buf, m1);
    1029              : 
    1030            6 :     if (dollars == 0)
    1031            0 :         appendStringInfoString(&buf, "zero");
    1032              : 
    1033            6 :     appendStringInfoString(&buf, dollars == 1 ? " dollar and " : " dollars and ");
    1034            6 :     append_num_word(&buf, m0);
    1035            6 :     appendStringInfoString(&buf, m0 == 1 ? " cent" : " cents");
    1036              : 
    1037              :     /* capitalize output */
    1038            6 :     buf.data[0] = pg_ascii_toupper((unsigned char) buf.data[0]);
    1039              : 
    1040              :     /* return as text datum */
    1041            6 :     res = cstring_to_text_with_len(buf.data, buf.len);
    1042            6 :     pfree(buf.data);
    1043            6 :     PG_RETURN_TEXT_P(res);
    1044              : }
    1045              : 
    1046              : 
    1047              : /* cash_numeric()
    1048              :  * Convert cash to numeric.
    1049              :  */
    1050              : Datum
    1051           12 : cash_numeric(PG_FUNCTION_ARGS)
    1052              : {
    1053           12 :     Cash        money = PG_GETARG_CASH(0);
    1054              :     Datum       result;
    1055              :     int         fpoint;
    1056           12 :     struct lconv *lconvert = PGLC_localeconv();
    1057              : 
    1058              :     /* see comments about frac_digits in cash_in() */
    1059           12 :     fpoint = lconvert->frac_digits;
    1060           12 :     if (fpoint < 0 || fpoint > 10)
    1061           12 :         fpoint = 2;
    1062              : 
    1063              :     /* convert the integral money value to numeric */
    1064           12 :     result = NumericGetDatum(int64_to_numeric(money));
    1065              : 
    1066              :     /* scale appropriately, if needed */
    1067           12 :     if (fpoint > 0)
    1068              :     {
    1069              :         int64       scale;
    1070              :         int         i;
    1071              :         Datum       numeric_scale;
    1072              :         Datum       quotient;
    1073              : 
    1074              :         /* compute required scale factor */
    1075           12 :         scale = 1;
    1076           36 :         for (i = 0; i < fpoint; i++)
    1077           24 :             scale *= 10;
    1078           12 :         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           12 :         numeric_scale = DirectFunctionCall2(numeric_round,
    1088              :                                             numeric_scale,
    1089              :                                             Int32GetDatum(fpoint));
    1090              : 
    1091              :         /* Now we can safely divide ... */
    1092           12 :         quotient = DirectFunctionCall2(numeric_div, result, numeric_scale);
    1093              : 
    1094              :         /* ... and forcibly round to exactly the intended number of digits */
    1095           12 :         result = DirectFunctionCall2(numeric_round,
    1096              :                                      quotient,
    1097              :                                      Int32GetDatum(fpoint));
    1098              :     }
    1099              : 
    1100           12 :     PG_RETURN_DATUM(result);
    1101              : }
    1102              : 
    1103              : /* numeric_cash()
    1104              :  * Convert numeric to cash.
    1105              :  */
    1106              : Datum
    1107            6 : numeric_cash(PG_FUNCTION_ARGS)
    1108              : {
    1109            6 :     Datum       amount = PG_GETARG_DATUM(0);
    1110              :     Cash        result;
    1111              :     int         fpoint;
    1112              :     int64       scale;
    1113              :     int         i;
    1114              :     Datum       numeric_scale;
    1115            6 :     struct lconv *lconvert = PGLC_localeconv();
    1116              : 
    1117              :     /* see comments about frac_digits in cash_in() */
    1118            6 :     fpoint = lconvert->frac_digits;
    1119            6 :     if (fpoint < 0 || fpoint > 10)
    1120            6 :         fpoint = 2;
    1121              : 
    1122              :     /* compute required scale factor */
    1123            6 :     scale = 1;
    1124           18 :     for (i = 0; i < fpoint; i++)
    1125           12 :         scale *= 10;
    1126              : 
    1127              :     /* multiply the input amount by scale factor */
    1128            6 :     numeric_scale = NumericGetDatum(int64_to_numeric(scale));
    1129            6 :     amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
    1130              : 
    1131              :     /* note that numeric_int8 will round to nearest integer for us */
    1132            6 :     result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
    1133              : 
    1134            6 :     PG_RETURN_CASH(result);
    1135              : }
    1136              : 
    1137              : /* int4_cash()
    1138              :  * Convert int4 (int) to cash
    1139              :  */
    1140              : Datum
    1141           21 : int4_cash(PG_FUNCTION_ARGS)
    1142              : {
    1143           21 :     int32       amount = PG_GETARG_INT32(0);
    1144              :     Cash        result;
    1145              :     int         fpoint;
    1146              :     int64       scale;
    1147              :     int         i;
    1148           21 :     struct lconv *lconvert = PGLC_localeconv();
    1149              : 
    1150              :     /* see comments about frac_digits in cash_in() */
    1151           21 :     fpoint = lconvert->frac_digits;
    1152           21 :     if (fpoint < 0 || fpoint > 10)
    1153           21 :         fpoint = 2;
    1154              : 
    1155              :     /* compute required scale factor */
    1156           21 :     scale = 1;
    1157           63 :     for (i = 0; i < fpoint; i++)
    1158           42 :         scale *= 10;
    1159              : 
    1160              :     /* compute amount * scale, checking for overflow */
    1161           21 :     result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
    1162              :                                                Int64GetDatum(scale)));
    1163              : 
    1164           21 :     PG_RETURN_CASH(result);
    1165              : }
    1166              : 
    1167              : /* int8_cash()
    1168              :  * Convert int8 (bigint) to cash
    1169              :  */
    1170              : Datum
    1171           12 : int8_cash(PG_FUNCTION_ARGS)
    1172              : {
    1173           12 :     int64       amount = PG_GETARG_INT64(0);
    1174              :     Cash        result;
    1175              :     int         fpoint;
    1176              :     int64       scale;
    1177              :     int         i;
    1178           12 :     struct lconv *lconvert = PGLC_localeconv();
    1179              : 
    1180              :     /* see comments about frac_digits in cash_in() */
    1181           12 :     fpoint = lconvert->frac_digits;
    1182           12 :     if (fpoint < 0 || fpoint > 10)
    1183           12 :         fpoint = 2;
    1184              : 
    1185              :     /* compute required scale factor */
    1186           12 :     scale = 1;
    1187           36 :     for (i = 0; i < fpoint; i++)
    1188           24 :         scale *= 10;
    1189              : 
    1190              :     /* compute amount * scale, checking for overflow */
    1191           12 :     result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
    1192              :                                                Int64GetDatum(scale)));
    1193              : 
    1194           12 :     PG_RETURN_CASH(result);
    1195              : }
        

Generated by: LCOV version 2.0-1