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

Generated by: LCOV version 1.13