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

Generated by: LCOV version 1.11