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

Generated by: LCOV version 1.14