Header And Logo

PostgreSQL
| The world's most advanced open source database.

cash.c

Go to the documentation of this file.
00001 /*
00002  * cash.c
00003  * Written by D'Arcy J.M. Cain
00004  * [email protected]
00005  * http://www.druid.net/darcy/
00006  *
00007  * Functions to allow input and output of money normally but store
00008  * and handle it as 64 bit ints
00009  *
00010  * A slightly modified version of this file and a discussion of the
00011  * workings can be found in the book "Software Solutions in C" by
00012  * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
00013  * this version handles 64 bit numbers and so can hold values up to
00014  * $92,233,720,368,547,758.07.
00015  *
00016  * src/backend/utils/adt/cash.c
00017  */
00018 
00019 #include "postgres.h"
00020 
00021 #include <limits.h>
00022 #include <ctype.h>
00023 #include <math.h>
00024 #include <locale.h>
00025 
00026 #include "libpq/pqformat.h"
00027 #include "utils/builtins.h"
00028 #include "utils/cash.h"
00029 #include "utils/int8.h"
00030 #include "utils/numeric.h"
00031 #include "utils/pg_locale.h"
00032 
00033 
00034 /*************************************************************************
00035  * Private routines
00036  ************************************************************************/
00037 
00038 static const char *
00039 num_word(Cash value)
00040 {
00041     static char buf[128];
00042     static const char *small[] = {
00043         "zero", "one", "two", "three", "four", "five", "six", "seven",
00044         "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
00045         "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
00046         "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
00047     };
00048     const char **big = small + 18;
00049     int         tu = value % 100;
00050 
00051     /* deal with the simple cases first */
00052     if (value <= 20)
00053         return small[value];
00054 
00055     /* is it an even multiple of 100? */
00056     if (!tu)
00057     {
00058         sprintf(buf, "%s hundred", small[value / 100]);
00059         return buf;
00060     }
00061 
00062     /* more than 99? */
00063     if (value > 99)
00064     {
00065         /* is it an even multiple of 10 other than 10? */
00066         if (value % 10 == 0 && tu > 10)
00067             sprintf(buf, "%s hundred %s",
00068                     small[value / 100], big[tu / 10]);
00069         else if (tu < 20)
00070             sprintf(buf, "%s hundred and %s",
00071                     small[value / 100], small[tu]);
00072         else
00073             sprintf(buf, "%s hundred %s %s",
00074                     small[value / 100], big[tu / 10], small[tu % 10]);
00075     }
00076     else
00077     {
00078         /* is it an even multiple of 10 other than 10? */
00079         if (value % 10 == 0 && tu > 10)
00080             sprintf(buf, "%s", big[tu / 10]);
00081         else if (tu < 20)
00082             sprintf(buf, "%s", small[tu]);
00083         else
00084             sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
00085     }
00086 
00087     return buf;
00088 }   /* num_word() */
00089 
00090 /* cash_in()
00091  * Convert a string to a cash data type.
00092  * Format is [$]###[,]###[.##]
00093  * Examples: 123.45 $123.45 $123,456.78
00094  *
00095  */
00096 Datum
00097 cash_in(PG_FUNCTION_ARGS)
00098 {
00099     char       *str = PG_GETARG_CSTRING(0);
00100     Cash        result;
00101     Cash        value = 0;
00102     Cash        dec = 0;
00103     Cash        sgn = 1;
00104     bool        seen_dot = false;
00105     const char *s = str;
00106     int         fpoint;
00107     char        dsymbol;
00108     const char *ssymbol,
00109                *psymbol,
00110                *nsymbol,
00111                *csymbol;
00112     struct lconv *lconvert = PGLC_localeconv();
00113 
00114     /*
00115      * frac_digits will be CHAR_MAX in some locales, notably C.  However, just
00116      * testing for == CHAR_MAX is risky, because of compilers like gcc that
00117      * "helpfully" let you alter the platform-standard definition of whether
00118      * char is signed or not.  If we are so unfortunate as to get compiled
00119      * with a nonstandard -fsigned-char or -funsigned-char switch, then our
00120      * idea of CHAR_MAX will not agree with libc's. The safest course is not
00121      * to test for CHAR_MAX at all, but to impose a range check for plausible
00122      * frac_digits values.
00123      */
00124     fpoint = lconvert->frac_digits;
00125     if (fpoint < 0 || fpoint > 10)
00126         fpoint = 2;             /* best guess in this case, I think */
00127 
00128     /* we restrict dsymbol to be a single byte, but not the other symbols */
00129     if (*lconvert->mon_decimal_point != '\0' &&
00130         lconvert->mon_decimal_point[1] == '\0')
00131         dsymbol = *lconvert->mon_decimal_point;
00132     else
00133         dsymbol = '.';
00134     if (*lconvert->mon_thousands_sep != '\0')
00135         ssymbol = lconvert->mon_thousands_sep;
00136     else    /* ssymbol should not equal dsymbol */
00137         ssymbol = (dsymbol != ',') ? "," : ".";
00138     csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
00139     psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
00140     nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
00141 
00142 #ifdef CASHDEBUG
00143     printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
00144            fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
00145 #endif
00146 
00147     /* we need to add all sorts of checking here.  For now just */
00148     /* strip all leading whitespace and any leading currency symbol */
00149     while (isspace((unsigned char) *s))
00150         s++;
00151     if (strncmp(s, csymbol, strlen(csymbol)) == 0)
00152         s += strlen(csymbol);
00153     while (isspace((unsigned char) *s))
00154         s++;
00155 
00156 #ifdef CASHDEBUG
00157     printf("cashin- string is '%s'\n", s);
00158 #endif
00159 
00160     /* a leading minus or paren signifies a negative number */
00161     /* again, better heuristics needed */
00162     /* XXX - doesn't properly check for balanced parens - djmc */
00163     if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
00164     {
00165         sgn = -1;
00166         s += strlen(nsymbol);
00167     }
00168     else if (*s == '(')
00169     {
00170         sgn = -1;
00171         s++;
00172     }
00173     else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
00174         s += strlen(psymbol);
00175 
00176 #ifdef CASHDEBUG
00177     printf("cashin- string is '%s'\n", s);
00178 #endif
00179 
00180     /* allow whitespace and currency symbol after the sign, too */
00181     while (isspace((unsigned char) *s))
00182         s++;
00183     if (strncmp(s, csymbol, strlen(csymbol)) == 0)
00184         s += strlen(csymbol);
00185     while (isspace((unsigned char) *s))
00186         s++;
00187 
00188 #ifdef CASHDEBUG
00189     printf("cashin- string is '%s'\n", s);
00190 #endif
00191 
00192     for (; *s; s++)
00193     {
00194         /* we look for digits as long as we have found less */
00195         /* than the required number of decimal places */
00196         if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
00197         {
00198             value = (value * 10) + (*s - '0');
00199 
00200             if (seen_dot)
00201                 dec++;
00202         }
00203         /* decimal point? then start counting fractions... */
00204         else if (*s == dsymbol && !seen_dot)
00205         {
00206             seen_dot = true;
00207         }
00208         /* ignore if "thousands" separator, else we're done */
00209         else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
00210             s += strlen(ssymbol) - 1;
00211         else
00212             break;
00213     }
00214 
00215     /* round off if there's another digit */
00216     if (isdigit((unsigned char) *s) && *s >= '5')
00217         value++;
00218 
00219     /* adjust for less than required decimal places */
00220     for (; dec < fpoint; dec++)
00221         value *= 10;
00222 
00223     /*
00224      * should only be trailing digits followed by whitespace, right paren,
00225      * trailing sign, and/or trailing currency symbol
00226      */
00227     while (isdigit((unsigned char) *s))
00228         s++;
00229 
00230     while (*s)
00231     {
00232         if (isspace((unsigned char) *s) || *s == ')')
00233             s++;
00234         else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
00235         {
00236             sgn = -1;
00237             s += strlen(nsymbol);
00238         }
00239         else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
00240             s += strlen(psymbol);
00241         else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
00242             s += strlen(csymbol);
00243         else
00244             ereport(ERROR,
00245                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00246                      errmsg("invalid input syntax for type money: \"%s\"",
00247                             str)));
00248     }
00249 
00250     result = value * sgn;
00251 
00252 #ifdef CASHDEBUG
00253     printf("cashin- result is " INT64_FORMAT "\n", result);
00254 #endif
00255 
00256     PG_RETURN_CASH(result);
00257 }
00258 
00259 
00260 /* cash_out()
00261  * Function to convert cash to a dollars and cents representation, using
00262  * the lc_monetary locale's formatting.
00263  */
00264 Datum
00265 cash_out(PG_FUNCTION_ARGS)
00266 {
00267     Cash        value = PG_GETARG_CASH(0);
00268     char       *result;
00269     char        buf[128];
00270     char       *bufptr;
00271     int         digit_pos;
00272     int         points,
00273                 mon_group;
00274     char        dsymbol;
00275     const char *ssymbol,
00276                *csymbol,
00277                *signsymbol;
00278     char        sign_posn,
00279                 cs_precedes,
00280                 sep_by_space;
00281     struct lconv *lconvert = PGLC_localeconv();
00282 
00283     /* see comments about frac_digits in cash_in() */
00284     points = lconvert->frac_digits;
00285     if (points < 0 || points > 10)
00286         points = 2;             /* best guess in this case, I think */
00287 
00288     /*
00289      * As with frac_digits, must apply a range check to mon_grouping to avoid
00290      * being fooled by variant CHAR_MAX values.
00291      */
00292     mon_group = *lconvert->mon_grouping;
00293     if (mon_group <= 0 || mon_group > 6)
00294         mon_group = 3;
00295 
00296     /* we restrict dsymbol to be a single byte, but not the other symbols */
00297     if (*lconvert->mon_decimal_point != '\0' &&
00298         lconvert->mon_decimal_point[1] == '\0')
00299         dsymbol = *lconvert->mon_decimal_point;
00300     else
00301         dsymbol = '.';
00302     if (*lconvert->mon_thousands_sep != '\0')
00303         ssymbol = lconvert->mon_thousands_sep;
00304     else    /* ssymbol should not equal dsymbol */
00305         ssymbol = (dsymbol != ',') ? "," : ".";
00306     csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
00307 
00308     if (value < 0)
00309     {
00310         /* make the amount positive for digit-reconstruction loop */
00311         value = -value;
00312         /* set up formatting data */
00313         signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
00314         sign_posn = lconvert->n_sign_posn;
00315         cs_precedes = lconvert->n_cs_precedes;
00316         sep_by_space = lconvert->n_sep_by_space;
00317     }
00318     else
00319     {
00320         signsymbol = lconvert->positive_sign;
00321         sign_posn = lconvert->p_sign_posn;
00322         cs_precedes = lconvert->p_cs_precedes;
00323         sep_by_space = lconvert->p_sep_by_space;
00324     }
00325 
00326     /* we build the digits+decimal-point+sep string right-to-left in buf[] */
00327     bufptr = buf + sizeof(buf) - 1;
00328     *bufptr = '\0';
00329 
00330     /*
00331      * Generate digits till there are no non-zero digits left and we emitted
00332      * at least one to the left of the decimal point.  digit_pos is the
00333      * current digit position, with zero as the digit just left of the decimal
00334      * point, increasing to the right.
00335      */
00336     digit_pos = points;
00337     do
00338     {
00339         if (points && digit_pos == 0)
00340         {
00341             /* insert decimal point, but not if value cannot be fractional */
00342             *(--bufptr) = dsymbol;
00343         }
00344         else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
00345         {
00346             /* insert thousands sep, but only to left of radix point */
00347             bufptr -= strlen(ssymbol);
00348             memcpy(bufptr, ssymbol, strlen(ssymbol));
00349         }
00350 
00351         *(--bufptr) = ((uint64) value % 10) + '0';
00352         value = ((uint64) value) / 10;
00353         digit_pos--;
00354     } while (value || digit_pos >= 0);
00355 
00356     /*----------
00357      * Now, attach currency symbol and sign symbol in the correct order.
00358      *
00359      * The POSIX spec defines these values controlling this code:
00360      *
00361      * p/n_sign_posn:
00362      *  0   Parentheses enclose the quantity and the currency_symbol.
00363      *  1   The sign string precedes the quantity and the currency_symbol.
00364      *  2   The sign string succeeds the quantity and the currency_symbol.
00365      *  3   The sign string precedes the currency_symbol.
00366      *  4   The sign string succeeds the currency_symbol.
00367      *
00368      * p/n_cs_precedes: 0 means currency symbol after value, else before it.
00369      *
00370      * p/n_sep_by_space:
00371      *  0   No <space> separates the currency symbol and value.
00372      *  1   If the currency symbol and sign string are adjacent, a <space>
00373      *      separates them from the value; otherwise, a <space> separates
00374      *      the currency symbol from the value.
00375      *  2   If the currency symbol and sign string are adjacent, a <space>
00376      *      separates them; otherwise, a <space> separates the sign string
00377      *      from the value.
00378      *----------
00379      */
00380     result = palloc(strlen(bufptr) + strlen(csymbol) + strlen(signsymbol) + 4);
00381 
00382     switch (sign_posn)
00383     {
00384         case 0:
00385             if (cs_precedes)
00386                 sprintf(result, "(%s%s%s)",
00387                         csymbol,
00388                         (sep_by_space == 1) ? " " : "",
00389                         bufptr);
00390             else
00391                 sprintf(result, "(%s%s%s)",
00392                         bufptr,
00393                         (sep_by_space == 1) ? " " : "",
00394                         csymbol);
00395             break;
00396         case 1:
00397         default:
00398             if (cs_precedes)
00399                 sprintf(result, "%s%s%s%s%s",
00400                         signsymbol,
00401                         (sep_by_space == 2) ? " " : "",
00402                         csymbol,
00403                         (sep_by_space == 1) ? " " : "",
00404                         bufptr);
00405             else
00406                 sprintf(result, "%s%s%s%s%s",
00407                         signsymbol,
00408                         (sep_by_space == 2) ? " " : "",
00409                         bufptr,
00410                         (sep_by_space == 1) ? " " : "",
00411                         csymbol);
00412             break;
00413         case 2:
00414             if (cs_precedes)
00415                 sprintf(result, "%s%s%s%s%s",
00416                         csymbol,
00417                         (sep_by_space == 1) ? " " : "",
00418                         bufptr,
00419                         (sep_by_space == 2) ? " " : "",
00420                         signsymbol);
00421             else
00422                 sprintf(result, "%s%s%s%s%s",
00423                         bufptr,
00424                         (sep_by_space == 1) ? " " : "",
00425                         csymbol,
00426                         (sep_by_space == 2) ? " " : "",
00427                         signsymbol);
00428             break;
00429         case 3:
00430             if (cs_precedes)
00431                 sprintf(result, "%s%s%s%s%s",
00432                         signsymbol,
00433                         (sep_by_space == 2) ? " " : "",
00434                         csymbol,
00435                         (sep_by_space == 1) ? " " : "",
00436                         bufptr);
00437             else
00438                 sprintf(result, "%s%s%s%s%s",
00439                         bufptr,
00440                         (sep_by_space == 1) ? " " : "",
00441                         signsymbol,
00442                         (sep_by_space == 2) ? " " : "",
00443                         csymbol);
00444             break;
00445         case 4:
00446             if (cs_precedes)
00447                 sprintf(result, "%s%s%s%s%s",
00448                         csymbol,
00449                         (sep_by_space == 2) ? " " : "",
00450                         signsymbol,
00451                         (sep_by_space == 1) ? " " : "",
00452                         bufptr);
00453             else
00454                 sprintf(result, "%s%s%s%s%s",
00455                         bufptr,
00456                         (sep_by_space == 1) ? " " : "",
00457                         csymbol,
00458                         (sep_by_space == 2) ? " " : "",
00459                         signsymbol);
00460             break;
00461     }
00462 
00463     PG_RETURN_CSTRING(result);
00464 }
00465 
00466 /*
00467  *      cash_recv           - converts external binary format to cash
00468  */
00469 Datum
00470 cash_recv(PG_FUNCTION_ARGS)
00471 {
00472     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
00473 
00474     PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
00475 }
00476 
00477 /*
00478  *      cash_send           - converts cash to binary format
00479  */
00480 Datum
00481 cash_send(PG_FUNCTION_ARGS)
00482 {
00483     Cash        arg1 = PG_GETARG_CASH(0);
00484     StringInfoData buf;
00485 
00486     pq_begintypsend(&buf);
00487     pq_sendint64(&buf, arg1);
00488     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
00489 }
00490 
00491 /*
00492  * Comparison functions
00493  */
00494 
00495 Datum
00496 cash_eq(PG_FUNCTION_ARGS)
00497 {
00498     Cash        c1 = PG_GETARG_CASH(0);
00499     Cash        c2 = PG_GETARG_CASH(1);
00500 
00501     PG_RETURN_BOOL(c1 == c2);
00502 }
00503 
00504 Datum
00505 cash_ne(PG_FUNCTION_ARGS)
00506 {
00507     Cash        c1 = PG_GETARG_CASH(0);
00508     Cash        c2 = PG_GETARG_CASH(1);
00509 
00510     PG_RETURN_BOOL(c1 != c2);
00511 }
00512 
00513 Datum
00514 cash_lt(PG_FUNCTION_ARGS)
00515 {
00516     Cash        c1 = PG_GETARG_CASH(0);
00517     Cash        c2 = PG_GETARG_CASH(1);
00518 
00519     PG_RETURN_BOOL(c1 < c2);
00520 }
00521 
00522 Datum
00523 cash_le(PG_FUNCTION_ARGS)
00524 {
00525     Cash        c1 = PG_GETARG_CASH(0);
00526     Cash        c2 = PG_GETARG_CASH(1);
00527 
00528     PG_RETURN_BOOL(c1 <= c2);
00529 }
00530 
00531 Datum
00532 cash_gt(PG_FUNCTION_ARGS)
00533 {
00534     Cash        c1 = PG_GETARG_CASH(0);
00535     Cash        c2 = PG_GETARG_CASH(1);
00536 
00537     PG_RETURN_BOOL(c1 > c2);
00538 }
00539 
00540 Datum
00541 cash_ge(PG_FUNCTION_ARGS)
00542 {
00543     Cash        c1 = PG_GETARG_CASH(0);
00544     Cash        c2 = PG_GETARG_CASH(1);
00545 
00546     PG_RETURN_BOOL(c1 >= c2);
00547 }
00548 
00549 Datum
00550 cash_cmp(PG_FUNCTION_ARGS)
00551 {
00552     Cash        c1 = PG_GETARG_CASH(0);
00553     Cash        c2 = PG_GETARG_CASH(1);
00554 
00555     if (c1 > c2)
00556         PG_RETURN_INT32(1);
00557     else if (c1 == c2)
00558         PG_RETURN_INT32(0);
00559     else
00560         PG_RETURN_INT32(-1);
00561 }
00562 
00563 
00564 /* cash_pl()
00565  * Add two cash values.
00566  */
00567 Datum
00568 cash_pl(PG_FUNCTION_ARGS)
00569 {
00570     Cash        c1 = PG_GETARG_CASH(0);
00571     Cash        c2 = PG_GETARG_CASH(1);
00572     Cash        result;
00573 
00574     result = c1 + c2;
00575 
00576     PG_RETURN_CASH(result);
00577 }
00578 
00579 
00580 /* cash_mi()
00581  * Subtract two cash values.
00582  */
00583 Datum
00584 cash_mi(PG_FUNCTION_ARGS)
00585 {
00586     Cash        c1 = PG_GETARG_CASH(0);
00587     Cash        c2 = PG_GETARG_CASH(1);
00588     Cash        result;
00589 
00590     result = c1 - c2;
00591 
00592     PG_RETURN_CASH(result);
00593 }
00594 
00595 
00596 /* cash_div_cash()
00597  * Divide cash by cash, returning float8.
00598  */
00599 Datum
00600 cash_div_cash(PG_FUNCTION_ARGS)
00601 {
00602     Cash        dividend = PG_GETARG_CASH(0);
00603     Cash        divisor = PG_GETARG_CASH(1);
00604     float8      quotient;
00605 
00606     if (divisor == 0)
00607         ereport(ERROR,
00608                 (errcode(ERRCODE_DIVISION_BY_ZERO),
00609                  errmsg("division by zero")));
00610 
00611     quotient = (float8) dividend / (float8) divisor;
00612     PG_RETURN_FLOAT8(quotient);
00613 }
00614 
00615 
00616 /* cash_mul_flt8()
00617  * Multiply cash by float8.
00618  */
00619 Datum
00620 cash_mul_flt8(PG_FUNCTION_ARGS)
00621 {
00622     Cash        c = PG_GETARG_CASH(0);
00623     float8      f = PG_GETARG_FLOAT8(1);
00624     Cash        result;
00625 
00626     result = c * f;
00627     PG_RETURN_CASH(result);
00628 }
00629 
00630 
00631 /* flt8_mul_cash()
00632  * Multiply float8 by cash.
00633  */
00634 Datum
00635 flt8_mul_cash(PG_FUNCTION_ARGS)
00636 {
00637     float8      f = PG_GETARG_FLOAT8(0);
00638     Cash        c = PG_GETARG_CASH(1);
00639     Cash        result;
00640 
00641     result = f * c;
00642     PG_RETURN_CASH(result);
00643 }
00644 
00645 
00646 /* cash_div_flt8()
00647  * Divide cash by float8.
00648  */
00649 Datum
00650 cash_div_flt8(PG_FUNCTION_ARGS)
00651 {
00652     Cash        c = PG_GETARG_CASH(0);
00653     float8      f = PG_GETARG_FLOAT8(1);
00654     Cash        result;
00655 
00656     if (f == 0.0)
00657         ereport(ERROR,
00658                 (errcode(ERRCODE_DIVISION_BY_ZERO),
00659                  errmsg("division by zero")));
00660 
00661     result = rint(c / f);
00662     PG_RETURN_CASH(result);
00663 }
00664 
00665 
00666 /* cash_mul_flt4()
00667  * Multiply cash by float4.
00668  */
00669 Datum
00670 cash_mul_flt4(PG_FUNCTION_ARGS)
00671 {
00672     Cash        c = PG_GETARG_CASH(0);
00673     float4      f = PG_GETARG_FLOAT4(1);
00674     Cash        result;
00675 
00676     result = c * f;
00677     PG_RETURN_CASH(result);
00678 }
00679 
00680 
00681 /* flt4_mul_cash()
00682  * Multiply float4 by cash.
00683  */
00684 Datum
00685 flt4_mul_cash(PG_FUNCTION_ARGS)
00686 {
00687     float4      f = PG_GETARG_FLOAT4(0);
00688     Cash        c = PG_GETARG_CASH(1);
00689     Cash        result;
00690 
00691     result = f * c;
00692     PG_RETURN_CASH(result);
00693 }
00694 
00695 
00696 /* cash_div_flt4()
00697  * Divide cash by float4.
00698  *
00699  */
00700 Datum
00701 cash_div_flt4(PG_FUNCTION_ARGS)
00702 {
00703     Cash        c = PG_GETARG_CASH(0);
00704     float4      f = PG_GETARG_FLOAT4(1);
00705     Cash        result;
00706 
00707     if (f == 0.0)
00708         ereport(ERROR,
00709                 (errcode(ERRCODE_DIVISION_BY_ZERO),
00710                  errmsg("division by zero")));
00711 
00712     result = rint(c / f);
00713     PG_RETURN_CASH(result);
00714 }
00715 
00716 
00717 /* cash_mul_int8()
00718  * Multiply cash by int8.
00719  */
00720 Datum
00721 cash_mul_int8(PG_FUNCTION_ARGS)
00722 {
00723     Cash        c = PG_GETARG_CASH(0);
00724     int64       i = PG_GETARG_INT64(1);
00725     Cash        result;
00726 
00727     result = c * i;
00728     PG_RETURN_CASH(result);
00729 }
00730 
00731 
00732 /* int8_mul_cash()
00733  * Multiply int8 by cash.
00734  */
00735 Datum
00736 int8_mul_cash(PG_FUNCTION_ARGS)
00737 {
00738     int64       i = PG_GETARG_INT64(0);
00739     Cash        c = PG_GETARG_CASH(1);
00740     Cash        result;
00741 
00742     result = i * c;
00743     PG_RETURN_CASH(result);
00744 }
00745 
00746 /* cash_div_int8()
00747  * Divide cash by 8-byte integer.
00748  */
00749 Datum
00750 cash_div_int8(PG_FUNCTION_ARGS)
00751 {
00752     Cash        c = PG_GETARG_CASH(0);
00753     int64       i = PG_GETARG_INT64(1);
00754     Cash        result;
00755 
00756     if (i == 0)
00757         ereport(ERROR,
00758                 (errcode(ERRCODE_DIVISION_BY_ZERO),
00759                  errmsg("division by zero")));
00760 
00761     result = rint(c / i);
00762 
00763     PG_RETURN_CASH(result);
00764 }
00765 
00766 
00767 /* cash_mul_int4()
00768  * Multiply cash by int4.
00769  */
00770 Datum
00771 cash_mul_int4(PG_FUNCTION_ARGS)
00772 {
00773     Cash        c = PG_GETARG_CASH(0);
00774     int32       i = PG_GETARG_INT32(1);
00775     Cash        result;
00776 
00777     result = c * i;
00778     PG_RETURN_CASH(result);
00779 }
00780 
00781 
00782 /* int4_mul_cash()
00783  * Multiply int4 by cash.
00784  */
00785 Datum
00786 int4_mul_cash(PG_FUNCTION_ARGS)
00787 {
00788     int32       i = PG_GETARG_INT32(0);
00789     Cash        c = PG_GETARG_CASH(1);
00790     Cash        result;
00791 
00792     result = i * c;
00793     PG_RETURN_CASH(result);
00794 }
00795 
00796 
00797 /* cash_div_int4()
00798  * Divide cash by 4-byte integer.
00799  *
00800  */
00801 Datum
00802 cash_div_int4(PG_FUNCTION_ARGS)
00803 {
00804     Cash        c = PG_GETARG_CASH(0);
00805     int32       i = PG_GETARG_INT32(1);
00806     Cash        result;
00807 
00808     if (i == 0)
00809         ereport(ERROR,
00810                 (errcode(ERRCODE_DIVISION_BY_ZERO),
00811                  errmsg("division by zero")));
00812 
00813     result = rint(c / i);
00814 
00815     PG_RETURN_CASH(result);
00816 }
00817 
00818 
00819 /* cash_mul_int2()
00820  * Multiply cash by int2.
00821  */
00822 Datum
00823 cash_mul_int2(PG_FUNCTION_ARGS)
00824 {
00825     Cash        c = PG_GETARG_CASH(0);
00826     int16       s = PG_GETARG_INT16(1);
00827     Cash        result;
00828 
00829     result = c * s;
00830     PG_RETURN_CASH(result);
00831 }
00832 
00833 /* int2_mul_cash()
00834  * Multiply int2 by cash.
00835  */
00836 Datum
00837 int2_mul_cash(PG_FUNCTION_ARGS)
00838 {
00839     int16       s = PG_GETARG_INT16(0);
00840     Cash        c = PG_GETARG_CASH(1);
00841     Cash        result;
00842 
00843     result = s * c;
00844     PG_RETURN_CASH(result);
00845 }
00846 
00847 /* cash_div_int2()
00848  * Divide cash by int2.
00849  *
00850  */
00851 Datum
00852 cash_div_int2(PG_FUNCTION_ARGS)
00853 {
00854     Cash        c = PG_GETARG_CASH(0);
00855     int16       s = PG_GETARG_INT16(1);
00856     Cash        result;
00857 
00858     if (s == 0)
00859         ereport(ERROR,
00860                 (errcode(ERRCODE_DIVISION_BY_ZERO),
00861                  errmsg("division by zero")));
00862 
00863     result = rint(c / s);
00864     PG_RETURN_CASH(result);
00865 }
00866 
00867 /* cashlarger()
00868  * Return larger of two cash values.
00869  */
00870 Datum
00871 cashlarger(PG_FUNCTION_ARGS)
00872 {
00873     Cash        c1 = PG_GETARG_CASH(0);
00874     Cash        c2 = PG_GETARG_CASH(1);
00875     Cash        result;
00876 
00877     result = (c1 > c2) ? c1 : c2;
00878 
00879     PG_RETURN_CASH(result);
00880 }
00881 
00882 /* cashsmaller()
00883  * Return smaller of two cash values.
00884  */
00885 Datum
00886 cashsmaller(PG_FUNCTION_ARGS)
00887 {
00888     Cash        c1 = PG_GETARG_CASH(0);
00889     Cash        c2 = PG_GETARG_CASH(1);
00890     Cash        result;
00891 
00892     result = (c1 < c2) ? c1 : c2;
00893 
00894     PG_RETURN_CASH(result);
00895 }
00896 
00897 /* cash_words()
00898  * This converts a int4 as well but to a representation using words
00899  * Obviously way North American centric - sorry
00900  */
00901 Datum
00902 cash_words(PG_FUNCTION_ARGS)
00903 {
00904     Cash        value = PG_GETARG_CASH(0);
00905     uint64      val;
00906     char        buf[256];
00907     char       *p = buf;
00908     Cash        m0;
00909     Cash        m1;
00910     Cash        m2;
00911     Cash        m3;
00912     Cash        m4;
00913     Cash        m5;
00914     Cash        m6;
00915 
00916     /* work with positive numbers */
00917     if (value < 0)
00918     {
00919         value = -value;
00920         strcpy(buf, "minus ");
00921         p += 6;
00922     }
00923     else
00924         buf[0] = '\0';
00925 
00926     /* Now treat as unsigned, to avoid trouble at INT_MIN */
00927     val = (uint64) value;
00928 
00929     m0 = val % INT64CONST(100); /* cents */
00930     m1 = (val / INT64CONST(100)) % 1000;        /* hundreds */
00931     m2 = (val / INT64CONST(100000)) % 1000;     /* thousands */
00932     m3 = (val / INT64CONST(100000000)) % 1000;  /* millions */
00933     m4 = (val / INT64CONST(100000000000)) % 1000;       /* billions */
00934     m5 = (val / INT64CONST(100000000000000)) % 1000;    /* trillions */
00935     m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
00936 
00937     if (m6)
00938     {
00939         strcat(buf, num_word(m6));
00940         strcat(buf, " quadrillion ");
00941     }
00942 
00943     if (m5)
00944     {
00945         strcat(buf, num_word(m5));
00946         strcat(buf, " trillion ");
00947     }
00948 
00949     if (m4)
00950     {
00951         strcat(buf, num_word(m4));
00952         strcat(buf, " billion ");
00953     }
00954 
00955     if (m3)
00956     {
00957         strcat(buf, num_word(m3));
00958         strcat(buf, " million ");
00959     }
00960 
00961     if (m2)
00962     {
00963         strcat(buf, num_word(m2));
00964         strcat(buf, " thousand ");
00965     }
00966 
00967     if (m1)
00968         strcat(buf, num_word(m1));
00969 
00970     if (!*p)
00971         strcat(buf, "zero");
00972 
00973     strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
00974     strcat(buf, num_word(m0));
00975     strcat(buf, m0 == 1 ? " cent" : " cents");
00976 
00977     /* capitalize output */
00978     buf[0] = pg_toupper((unsigned char) buf[0]);
00979 
00980     /* return as text datum */
00981     PG_RETURN_TEXT_P(cstring_to_text(buf));
00982 }
00983 
00984 
00985 /* cash_numeric()
00986  * Convert cash to numeric.
00987  */
00988 Datum
00989 cash_numeric(PG_FUNCTION_ARGS)
00990 {
00991     Cash        money = PG_GETARG_CASH(0);
00992     Numeric     result;
00993     int         fpoint;
00994     int64       scale;
00995     int         i;
00996     Datum       amount;
00997     Datum       numeric_scale;
00998     Datum       quotient;
00999     struct lconv *lconvert = PGLC_localeconv();
01000 
01001     /* see comments about frac_digits in cash_in() */
01002     fpoint = lconvert->frac_digits;
01003     if (fpoint < 0 || fpoint > 10)
01004         fpoint = 2;
01005 
01006     /* compute required scale factor */
01007     scale = 1;
01008     for (i = 0; i < fpoint; i++)
01009         scale *= 10;
01010 
01011     /* form the result as money / scale */
01012     amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
01013     numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
01014     quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);
01015 
01016     /* forcibly round to exactly the intended number of digits */
01017     result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
01018                                                  quotient,
01019                                                  Int32GetDatum(fpoint)));
01020 
01021     PG_RETURN_NUMERIC(result);
01022 }
01023 
01024 /* numeric_cash()
01025  * Convert numeric to cash.
01026  */
01027 Datum
01028 numeric_cash(PG_FUNCTION_ARGS)
01029 {
01030     Datum       amount = PG_GETARG_DATUM(0);
01031     Cash        result;
01032     int         fpoint;
01033     int64       scale;
01034     int         i;
01035     Datum       numeric_scale;
01036     struct lconv *lconvert = PGLC_localeconv();
01037 
01038     /* see comments about frac_digits in cash_in() */
01039     fpoint = lconvert->frac_digits;
01040     if (fpoint < 0 || fpoint > 10)
01041         fpoint = 2;
01042 
01043     /* compute required scale factor */
01044     scale = 1;
01045     for (i = 0; i < fpoint; i++)
01046         scale *= 10;
01047 
01048     /* multiply the input amount by scale factor */
01049     numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
01050     amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
01051 
01052     /* note that numeric_int8 will round to nearest integer for us */
01053     result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
01054 
01055     PG_RETURN_CASH(result);
01056 }
01057 
01058 /* int4_cash()
01059  * Convert int4 (int) to cash
01060  */
01061 Datum
01062 int4_cash(PG_FUNCTION_ARGS)
01063 {
01064     int32       amount = PG_GETARG_INT32(0);
01065     Cash        result;
01066     int         fpoint;
01067     int64       scale;
01068     int         i;
01069     struct lconv *lconvert = PGLC_localeconv();
01070 
01071     /* see comments about frac_digits in cash_in() */
01072     fpoint = lconvert->frac_digits;
01073     if (fpoint < 0 || fpoint > 10)
01074         fpoint = 2;
01075 
01076     /* compute required scale factor */
01077     scale = 1;
01078     for (i = 0; i < fpoint; i++)
01079         scale *= 10;
01080 
01081     /* compute amount * scale, checking for overflow */
01082     result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
01083                                                Int64GetDatum(scale)));
01084 
01085     PG_RETURN_CASH(result);
01086 }
01087 
01088 /* int8_cash()
01089  * Convert int8 (bigint) to cash
01090  */
01091 Datum
01092 int8_cash(PG_FUNCTION_ARGS)
01093 {
01094     int64       amount = PG_GETARG_INT64(0);
01095     Cash        result;
01096     int         fpoint;
01097     int64       scale;
01098     int         i;
01099     struct lconv *lconvert = PGLC_localeconv();
01100 
01101     /* see comments about frac_digits in cash_in() */
01102     fpoint = lconvert->frac_digits;
01103     if (fpoint < 0 || fpoint > 10)
01104         fpoint = 2;
01105 
01106     /* compute required scale factor */
01107     scale = 1;
01108     for (i = 0; i < fpoint; i++)
01109         scale *= 10;
01110 
01111     /* compute amount * scale, checking for overflow */
01112     result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
01113                                                Int64GetDatum(scale)));
01114 
01115     PG_RETURN_CASH(result);
01116 }