/*
* cash.c
* Written by D'Arcy J.M. Cain
+ * darcy@druid.net
+ * http://www.druid.net/darcy/
*
* Functions to allow input and output of money normally but store
- * and handle it as int4s
+ * and handle it as 64 bit ints
*
* A slightly modified version of this file and a discussion of the
* workings can be found in the book "Software Solutions in C" by
- * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
+ * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
+ * this version handles 64 bit numbers and so can hold values up to
+ * $92,233,720,368,547,758.07.
*
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.24 1998/06/15 19:29:32 momjian Exp $
+ * src/backend/utils/adt/cash.c
*/
-#include <stdio.h>
-#include <string.h>
+#include "postgres.h"
+
#include <limits.h>
#include <ctype.h>
-#include <locale.h>
+#include <math.h>
-#include "postgres.h"
-#include "miscadmin.h"
+#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/cash.h"
+#include "utils/int8.h"
+#include "utils/numeric.h"
+#include "utils/pg_locale.h"
+
+
+/*************************************************************************
+ * Private routines
+ ************************************************************************/
-static const char *num_word(Cash value);
+static const char *
+num_word(Cash value)
+{
+ static char buf[128];
+ static const char *small[] = {
+ "zero", "one", "two", "three", "four", "five", "six", "seven",
+ "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
+ "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
+ };
+ const char **big = small + 18;
+ int tu = value % 100;
-/* when we go to 64 bit values we will have to modify this */
-#define CASH_BUFSZ 24
+ /* deal with the simple cases first */
+ if (value <= 20)
+ return small[value];
-#define TERMINATOR (CASH_BUFSZ - 1)
-#define LAST_PAREN (TERMINATOR - 1)
-#define LAST_DIGIT (LAST_PAREN - 1)
+ /* is it an even multiple of 100? */
+ if (!tu)
+ {
+ sprintf(buf, "%s hundred", small[value / 100]);
+ return buf;
+ }
-#ifdef USE_LOCALE
-static struct lconv *lconvert = NULL;
-#endif
+ /* more than 99? */
+ if (value > 99)
+ {
+ /* is it an even multiple of 10 other than 10? */
+ if (value % 10 == 0 && tu > 10)
+ sprintf(buf, "%s hundred %s",
+ small[value / 100], big[tu / 10]);
+ else if (tu < 20)
+ sprintf(buf, "%s hundred and %s",
+ small[value / 100], small[tu]);
+ else
+ sprintf(buf, "%s hundred %s %s",
+ small[value / 100], big[tu / 10], small[tu % 10]);
+ }
+ else
+ {
+ /* is it an even multiple of 10 other than 10? */
+ if (value % 10 == 0 && tu > 10)
+ sprintf(buf, "%s", big[tu / 10]);
+ else if (tu < 20)
+ sprintf(buf, "%s", small[tu]);
+ else
+ sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
+ }
+
+ return buf;
+} /* num_word() */
/* cash_in()
* Convert a string to a cash data type.
* Format is [$]###[,]###[.##]
* Examples: 123.45 $123.45 $123,456.78
*
- * This is currently implemented as a 32-bit integer.
- * XXX HACK It looks as though some of the symbols for
- * monetary values returned by localeconv() can be multiple
- * bytes/characters. This code assumes one byte only. - tgl 97/04/14
- * XXX UNHACK Allow the currency symbol to be multi-byte.
- * - thomas 1998-03-01
*/
-Cash *
-cash_in(const char *str)
+Datum
+cash_in(PG_FUNCTION_ARGS)
{
- Cash *result;
-
+ char *str = PG_GETARG_CSTRING(0);
+ Cash result;
Cash value = 0;
Cash dec = 0;
Cash sgn = 1;
- int seen_dot = 0;
+ bool seen_dot = false;
const char *s = str;
int fpoint;
- char *csymbol;
- char dsymbol,
- ssymbol,
- psymbol,
- *nsymbol;
-
-#ifdef USE_LOCALE
-#ifdef CASHDEBUG
- setlocale(LC_ALL, "");
- lconvert = localeconv();
-#endif
- if (lconvert == NULL)
- lconvert = localeconv();
-
- /* frac_digits in the C locale seems to return CHAR_MAX */
- /* best guess is 2 in this case I think */
- fpoint = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
-
- dsymbol = ((*lconvert->mon_decimal_point != '\0')? *lconvert->mon_decimal_point: '.');
- ssymbol = ((*lconvert->mon_thousands_sep != '\0')? *lconvert->mon_thousands_sep: ',');
- csymbol = ((*lconvert->currency_symbol != '\0')? lconvert->currency_symbol: "$");
- psymbol = ((*lconvert->positive_sign != '\0')? *lconvert->positive_sign: '+');
- nsymbol = ((*lconvert->negative_sign != '\0')? lconvert->negative_sign: "-");
-#else
- fpoint = 2;
- dsymbol = '.';
- ssymbol = ',';
- csymbol = "$";
- psymbol = '+';
- nsymbol = "-";
-#endif
+ char dsymbol;
+ const char *ssymbol,
+ *psymbol,
+ *nsymbol,
+ *csymbol;
+ struct lconv *lconvert = PGLC_localeconv();
+
+ /*
+ * frac_digits will be CHAR_MAX in some locales, notably C. However, just
+ * testing for == CHAR_MAX is risky, because of compilers like gcc that
+ * "helpfully" let you alter the platform-standard definition of whether
+ * char is signed or not. If we are so unfortunate as to get compiled
+ * with a nonstandard -fsigned-char or -funsigned-char switch, then our
+ * idea of CHAR_MAX will not agree with libc's. The safest course is not
+ * to test for CHAR_MAX at all, but to impose a range check for plausible
+ * frac_digits values.
+ */
+ fpoint = lconvert->frac_digits;
+ if (fpoint < 0 || fpoint > 10)
+ fpoint = 2; /* best guess in this case, I think */
+
+ /* we restrict dsymbol to be a single byte, but not the other symbols */
+ if (*lconvert->mon_decimal_point != '\0' &&
+ lconvert->mon_decimal_point[1] == '\0')
+ dsymbol = *lconvert->mon_decimal_point;
+ else
+ dsymbol = '.';
+ if (*lconvert->mon_thousands_sep != '\0')
+ ssymbol = lconvert->mon_thousands_sep;
+ else /* ssymbol should not equal dsymbol */
+ ssymbol = (dsymbol != ',') ? "," : ".";
+ csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
+ psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
+ nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
#ifdef CASHDEBUG
-printf( "cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'\n",
- fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
+ printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
+ fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
#endif
/* we need to add all sorts of checking here. For now just */
/* strip all leading whitespace and any leading currency symbol */
- while (isspace(*s)) s++;
- if (strncmp(s,csymbol,strlen(csymbol)) == 0) s += strlen(csymbol);
+ while (isspace((unsigned char) *s))
+ s++;
+ if (strncmp(s, csymbol, strlen(csymbol)) == 0)
+ s += strlen(csymbol);
+ while (isspace((unsigned char) *s))
+ s++;
#ifdef CASHDEBUG
-printf( "cashin- string is '%s'\n", s);
+ printf("cashin- string is '%s'\n", s);
#endif
/* a leading minus or paren signifies a negative number */
/* again, better heuristics needed */
- if (strncmp(s,nsymbol,strlen(nsymbol)) == 0)
+ /* XXX - doesn't properly check for balanced parens - djmc */
+ if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
{
sgn = -1;
s += strlen(nsymbol);
-#ifdef CASHDEBUG
-printf( "cashin- negative symbol; string is '%s'\n", s);
-#endif
}
else if (*s == '(')
{
sgn = -1;
s++;
-
}
- else if (*s == psymbol)
- s++;
+ else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
+ s += strlen(psymbol);
#ifdef CASHDEBUG
-printf( "cashin- string is '%s'\n", s);
+ printf("cashin- string is '%s'\n", s);
#endif
- while (isspace(*s)) s++;
- if (strncmp(s,csymbol,strlen(csymbol)) == 0) s += strlen(csymbol);
+ /* allow whitespace and currency symbol after the sign, too */
+ while (isspace((unsigned char) *s))
+ s++;
+ if (strncmp(s, csymbol, strlen(csymbol)) == 0)
+ s += strlen(csymbol);
+ while (isspace((unsigned char) *s))
+ s++;
#ifdef CASHDEBUG
-printf( "cashin- string is '%s'\n", s);
+ printf("cashin- string is '%s'\n", s);
#endif
- for (;; s++)
+ /*
+ * We accumulate the absolute amount in "value" and then apply the sign at
+ * the end. (The sign can appear before or after the digits, so it would
+ * be more complicated to do otherwise.) Because of the larger range of
+ * negative signed integers, we build "value" in the negative and then
+ * flip the sign at the end, catching most-negative-number overflow if
+ * necessary.
+ */
+
+ for (; *s; s++)
{
- /* we look for digits as int4 as we have less */
+ /* we look for digits as long as we have found less */
/* than the required number of decimal places */
- if (isdigit(*s) && dec < fpoint)
+ if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
{
- value = (value * 10) + *s - '0';
+ Cash newvalue = (value * 10) - (*s - '0');
+
+ if (newvalue / 10 != value)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value \"%s\" is out of range for type %s",
+ str, "money")));
+
+ value = newvalue;
if (seen_dot)
dec++;
-
- /* decimal point? then start counting fractions... */
}
+ /* decimal point? then start counting fractions... */
else if (*s == dsymbol && !seen_dot)
{
- seen_dot = 1;
-
- /* "thousands" separator? then skip... */
- }
- else if (*s == ssymbol)
- {
-
+ seen_dot = true;
}
+ /* ignore if "thousands" separator, else we're done */
+ else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
+ s += strlen(ssymbol) - 1;
else
- {
- /* round off */
- if (isdigit(*s) && *s >= '5')
- value++;
+ break;
+ }
- /* adjust for less than required decimal places */
- for (; dec < fpoint; dec++)
- value *= 10;
+ /* round off if there's another digit */
+ if (isdigit((unsigned char) *s) && *s >= '5')
+ value--; /* remember we build the value in the negative */
- break;
- }
+ if (value > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value \"%s\" is out of range for type %s",
+ str, "money")));
+
+ /* adjust for less than required decimal places */
+ for (; dec < fpoint; dec++)
+ {
+ Cash newvalue = value * 10;
+
+ if (newvalue / 10 != value)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value \"%s\" is out of range for type %s",
+ str, "money")));
+
+ value = newvalue;
}
- while (isspace(*s) || *s == '0' || *s == ')')
+ /*
+ * should only be trailing digits followed by whitespace, right paren,
+ * trailing sign, and/or trailing currency symbol
+ */
+ while (isdigit((unsigned char) *s))
s++;
- if (*s != '\0')
- elog(ERROR, "Bad money external representation %s", str);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't input cash '%s'", str);
+ while (*s)
+ {
+ if (isspace((unsigned char) *s) || *s == ')')
+ s++;
+ else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
+ {
+ sgn = -1;
+ s += strlen(nsymbol);
+ }
+ else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
+ s += strlen(psymbol);
+ else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
+ s += strlen(csymbol);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "money", str)));
+ }
- *result = (value * sgn);
+ /* If the value is supposed to be positive, flip the sign, but check for
+ * the most negative number. */
+ if (sgn > 0)
+ {
+ result = -value;
+ if (result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value \"%s\" is out of range for type %s",
+ str, "money")));
+ }
+ else
+ result = value;
#ifdef CASHDEBUG
-printf( "cashin- result is %d\n", *result);
+ printf("cashin- result is " INT64_FORMAT "\n", result);
#endif
- return (result);
-} /* cash_in() */
+ PG_RETURN_CASH(result);
+}
/* cash_out()
- * Function to convert cash to a dollars and cents representation.
- * XXX HACK This code appears to assume US conventions for
- * positive-valued amounts. - tgl 97/04/14
+ * Function to convert cash to a dollars and cents representation, using
+ * the lc_monetary locale's formatting.
*/
-const char *
-cash_out(Cash *in_value)
+Datum
+cash_out(PG_FUNCTION_ARGS)
{
- Cash value = *in_value;
+ Cash value = PG_GETARG_CASH(0);
char *result;
- char buf[CASH_BUFSZ];
- int minus = 0;
- int count = LAST_DIGIT;
- int point_pos;
- int comma_position = 0;
- char mon_group,
- comma,
- points;
- char *csymbol,
- dsymbol,
- *nsymbol;
- char convention;
-
-#ifdef USE_LOCALE
- if (lconvert == NULL)
- lconvert = localeconv();
-
+ char buf[128];
+ char *bufptr;
+ int digit_pos;
+ int points,
+ mon_group;
+ char dsymbol;
+ const char *ssymbol,
+ *csymbol,
+ *signsymbol;
+ char sign_posn,
+ cs_precedes,
+ sep_by_space;
+ struct lconv *lconvert = PGLC_localeconv();
+
+ /* see comments about frac_digits in cash_in() */
+ points = lconvert->frac_digits;
+ if (points < 0 || points > 10)
+ points = 2; /* best guess in this case, I think */
+
+ /*
+ * As with frac_digits, must apply a range check to mon_grouping to avoid
+ * being fooled by variant CHAR_MAX values.
+ */
mon_group = *lconvert->mon_grouping;
- comma = ((*lconvert->mon_thousands_sep != '\0')? *lconvert->mon_thousands_sep: ',');
- /* frac_digits in the C locale seems to return CHAR_MAX */
- /* best guess is 2 in this case I think */
- points = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
- convention = lconvert->n_sign_posn;
- dsymbol = ((*lconvert->mon_decimal_point != '\0')? *lconvert->mon_decimal_point: '.');
- csymbol = ((*lconvert->currency_symbol != '\0')? lconvert->currency_symbol: "$");
- nsymbol = ((*lconvert->negative_sign != '\0')? lconvert->negative_sign: "-");
-#else
- mon_group = 3;
- comma = ',';
- csymbol = "$";
- dsymbol = '.';
- nsymbol = "-";
- points = 2;
- convention = 0;
-#endif
-
- point_pos = LAST_DIGIT - points;
-
- /* We're playing a little fast and loose with this. Shoot me. */
- /* Not me, that was the other guy. Haven't fixed it yet - thomas */
- if (!mon_group || mon_group == CHAR_MAX)
+ if (mon_group <= 0 || mon_group > 6)
mon_group = 3;
- /* allow more than three decimal points and separate them */
- if (comma)
- {
- point_pos -= (points - 1) / mon_group;
- comma_position = point_pos % (mon_group + 1);
- }
+ /* we restrict dsymbol to be a single byte, but not the other symbols */
+ if (*lconvert->mon_decimal_point != '\0' &&
+ lconvert->mon_decimal_point[1] == '\0')
+ dsymbol = *lconvert->mon_decimal_point;
+ else
+ dsymbol = '.';
+ if (*lconvert->mon_thousands_sep != '\0')
+ ssymbol = lconvert->mon_thousands_sep;
+ else /* ssymbol should not equal dsymbol */
+ ssymbol = (dsymbol != ',') ? "," : ".";
+ csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
- /* we work with positive amounts and add the minus sign at the end */
if (value < 0)
{
- minus = 1;
- value *= -1;
+ /* make the amount positive for digit-reconstruction loop */
+ value = -value;
+ /* set up formatting data */
+ signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
+ sign_posn = lconvert->n_sign_posn;
+ cs_precedes = lconvert->n_cs_precedes;
+ sep_by_space = lconvert->n_sep_by_space;
+ }
+ else
+ {
+ signsymbol = lconvert->positive_sign;
+ sign_posn = lconvert->p_sign_posn;
+ cs_precedes = lconvert->p_cs_precedes;
+ sep_by_space = lconvert->p_sep_by_space;
}
- /* allow for trailing negative strings */
- MemSet(buf, ' ', CASH_BUFSZ);
- buf[TERMINATOR] = buf[LAST_PAREN] = '\0';
-
- while (value || count > (point_pos - 2))
+ /* we build the digits+decimal-point+sep string right-to-left in buf[] */
+ bufptr = buf + sizeof(buf) - 1;
+ *bufptr = '\0';
+
+ /*
+ * Generate digits till there are no non-zero digits left and we emitted
+ * at least one to the left of the decimal point. digit_pos is the
+ * current digit position, with zero as the digit just left of the decimal
+ * point, increasing to the right.
+ */
+ digit_pos = points;
+ do
{
- if (points && count == point_pos)
- buf[count--] = dsymbol;
- else if (comma && count % (mon_group + 1) == comma_position)
- buf[count--] = comma;
+ if (points && digit_pos == 0)
+ {
+ /* insert decimal point, but not if value cannot be fractional */
+ *(--bufptr) = dsymbol;
+ }
+ else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
+ {
+ /* insert thousands sep, but only to left of radix point */
+ bufptr -= strlen(ssymbol);
+ memcpy(bufptr, ssymbol, strlen(ssymbol));
+ }
- buf[count--] = (value % 10) + '0';
- value /= 10;
+ *(--bufptr) = ((uint64) value % 10) + '0';
+ value = ((uint64) value) / 10;
+ digit_pos--;
+ } while (value || digit_pos >= 0);
+
+ /*----------
+ * Now, attach currency symbol and sign symbol in the correct order.
+ *
+ * The POSIX spec defines these values controlling this code:
+ *
+ * p/n_sign_posn:
+ * 0 Parentheses enclose the quantity and the currency_symbol.
+ * 1 The sign string precedes the quantity and the currency_symbol.
+ * 2 The sign string succeeds the quantity and the currency_symbol.
+ * 3 The sign string precedes the currency_symbol.
+ * 4 The sign string succeeds the currency_symbol.
+ *
+ * p/n_cs_precedes: 0 means currency symbol after value, else before it.
+ *
+ * p/n_sep_by_space:
+ * 0 No <space> separates the currency symbol and value.
+ * 1 If the currency symbol and sign string are adjacent, a <space>
+ * separates them from the value; otherwise, a <space> separates
+ * the currency symbol from the value.
+ * 2 If the currency symbol and sign string are adjacent, a <space>
+ * separates them; otherwise, a <space> separates the sign string
+ * from the value.
+ *----------
+ */
+ switch (sign_posn)
+ {
+ case 0:
+ if (cs_precedes)
+ result = psprintf("(%s%s%s)",
+ csymbol,
+ (sep_by_space == 1) ? " " : "",
+ bufptr);
+ else
+ result = psprintf("(%s%s%s)",
+ bufptr,
+ (sep_by_space == 1) ? " " : "",
+ csymbol);
+ break;
+ case 1:
+ default:
+ if (cs_precedes)
+ result = psprintf("%s%s%s%s%s",
+ signsymbol,
+ (sep_by_space == 2) ? " " : "",
+ csymbol,
+ (sep_by_space == 1) ? " " : "",
+ bufptr);
+ else
+ result = psprintf("%s%s%s%s%s",
+ signsymbol,
+ (sep_by_space == 2) ? " " : "",
+ bufptr,
+ (sep_by_space == 1) ? " " : "",
+ csymbol);
+ break;
+ case 2:
+ if (cs_precedes)
+ result = psprintf("%s%s%s%s%s",
+ csymbol,
+ (sep_by_space == 1) ? " " : "",
+ bufptr,
+ (sep_by_space == 2) ? " " : "",
+ signsymbol);
+ else
+ result = psprintf("%s%s%s%s%s",
+ bufptr,
+ (sep_by_space == 1) ? " " : "",
+ csymbol,
+ (sep_by_space == 2) ? " " : "",
+ signsymbol);
+ break;
+ case 3:
+ if (cs_precedes)
+ result = psprintf("%s%s%s%s%s",
+ signsymbol,
+ (sep_by_space == 2) ? " " : "",
+ csymbol,
+ (sep_by_space == 1) ? " " : "",
+ bufptr);
+ else
+ result = psprintf("%s%s%s%s%s",
+ bufptr,
+ (sep_by_space == 1) ? " " : "",
+ signsymbol,
+ (sep_by_space == 2) ? " " : "",
+ csymbol);
+ break;
+ case 4:
+ if (cs_precedes)
+ result = psprintf("%s%s%s%s%s",
+ csymbol,
+ (sep_by_space == 2) ? " " : "",
+ signsymbol,
+ (sep_by_space == 1) ? " " : "",
+ bufptr);
+ else
+ result = psprintf("%s%s%s%s%s",
+ bufptr,
+ (sep_by_space == 1) ? " " : "",
+ csymbol,
+ (sep_by_space == 2) ? " " : "",
+ signsymbol);
+ break;
}
- strncpy((buf+count-strlen(csymbol)+1),csymbol,strlen(csymbol));
- count -= strlen(csymbol)-1;
+ PG_RETURN_CSTRING(result);
+}
- if (buf[LAST_DIGIT] == ',')
- buf[LAST_DIGIT] = buf[LAST_PAREN];
+/*
+ * cash_recv - converts external binary format to cash
+ */
+Datum
+cash_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- /* see if we need to signify negative amount */
- if (minus)
- {
- if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol))))
- elog(ERROR, "Memory allocation failed, can't output cash", NULL);
-
- /* Position code of 0 means use parens */
- if (convention == 0)
- sprintf(result, "(%s)", buf + count);
- else if (convention == 2)
- sprintf(result, "%s%s", buf + count, nsymbol);
- else
- sprintf(result, "%s%s", nsymbol, buf + count);
- }
- else
- {
- if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count)))
- elog(ERROR, "Memory allocation failed, can't output cash", NULL);
+ PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
+}
- strcpy(result, buf + count);
- }
+/*
+ * cash_send - converts cash to binary format
+ */
+Datum
+cash_send(PG_FUNCTION_ARGS)
+{
+ Cash arg1 = PG_GETARG_CASH(0);
+ StringInfoData buf;
+
+ pq_begintypsend(&buf);
+ pq_sendint64(&buf, arg1);
+ PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * Comparison functions
+ */
- return (result);
-} /* cash_out() */
+Datum
+cash_eq(PG_FUNCTION_ARGS)
+{
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
+ PG_RETURN_BOOL(c1 == c2);
+}
-bool
-cash_eq(Cash *c1, Cash *c2)
+Datum
+cash_ne(PG_FUNCTION_ARGS)
{
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (FALSE);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
- return (*c1 == *c2);
-} /* cash_eq() */
+ PG_RETURN_BOOL(c1 != c2);
+}
-bool
-cash_ne(Cash *c1, Cash *c2)
+Datum
+cash_lt(PG_FUNCTION_ARGS)
{
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (FALSE);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
- return (*c1 != *c2);
-} /* cash_ne() */
+ PG_RETURN_BOOL(c1 < c2);
+}
-bool
-cash_lt(Cash *c1, Cash *c2)
+Datum
+cash_le(PG_FUNCTION_ARGS)
{
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (FALSE);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
- return (*c1 < *c2);
-} /* cash_lt() */
+ PG_RETURN_BOOL(c1 <= c2);
+}
-bool
-cash_le(Cash *c1, Cash *c2)
+Datum
+cash_gt(PG_FUNCTION_ARGS)
{
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (FALSE);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
- return (*c1 <= *c2);
-} /* cash_le() */
+ PG_RETURN_BOOL(c1 > c2);
+}
-bool
-cash_gt(Cash *c1, Cash *c2)
+Datum
+cash_ge(PG_FUNCTION_ARGS)
{
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (FALSE);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
- return (*c1 > *c2);
-} /* cash_gt() */
+ PG_RETURN_BOOL(c1 >= c2);
+}
-bool
-cash_ge(Cash *c1, Cash *c2)
+Datum
+cash_cmp(PG_FUNCTION_ARGS)
{
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (FALSE);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
- return (*c1 >= *c2);
-} /* cash_ge() */
+ if (c1 > c2)
+ PG_RETURN_INT32(1);
+ else if (c1 == c2)
+ PG_RETURN_INT32(0);
+ else
+ PG_RETURN_INT32(-1);
+}
/* cash_pl()
* Add two cash values.
*/
-Cash *
-cash_pl(Cash *c1, Cash *c2)
+Datum
+cash_pl(PG_FUNCTION_ARGS)
{
- Cash *result;
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
+ Cash result;
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (NULL);
+ result = c1 + c2;
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't add cash", NULL);
-
- *result = (*c1 + *c2);
-
- return (result);
-} /* cash_pl() */
+ PG_RETURN_CASH(result);
+}
/* cash_mi()
* Subtract two cash values.
*/
-Cash *
-cash_mi(Cash *c1, Cash *c2)
+Datum
+cash_mi(PG_FUNCTION_ARGS)
{
- Cash *result;
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
+ Cash result;
+
+ result = c1 - c2;
+
+ PG_RETURN_CASH(result);
+}
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (NULL);
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't subtract cash", NULL);
+/* cash_div_cash()
+ * Divide cash by cash, returning float8.
+ */
+Datum
+cash_div_cash(PG_FUNCTION_ARGS)
+{
+ Cash dividend = PG_GETARG_CASH(0);
+ Cash divisor = PG_GETARG_CASH(1);
+ float8 quotient;
- *result = (*c1 - *c2);
+ if (divisor == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
- return (result);
-} /* cash_mi() */
+ quotient = (float8) dividend / (float8) divisor;
+ PG_RETURN_FLOAT8(quotient);
+}
/* cash_mul_flt8()
* Multiply cash by float8.
*/
-Cash *
-cash_mul_flt8(Cash *c, float8 *f)
+Datum
+cash_mul_flt8(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(f) || !PointerIsValid(c))
- return (NULL);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
+ Cash c = PG_GETARG_CASH(0);
+ float8 f = PG_GETARG_FLOAT8(1);
+ Cash result;
- *result = ((*f) * (*c));
-
- return (result);
-} /* cash_mul_flt8() */
+ result = c * f;
+ PG_RETURN_CASH(result);
+}
/* flt8_mul_cash()
* Multiply float8 by cash.
*/
-Cash *
-flt8_mul_cash(float8 *f, Cash *c)
+Datum
+flt8_mul_cash(PG_FUNCTION_ARGS)
{
- return (cash_mul_flt8(c, f));
-} /* flt8_mul_cash() */
+ float8 f = PG_GETARG_FLOAT8(0);
+ Cash c = PG_GETARG_CASH(1);
+ Cash result;
+
+ result = f * c;
+ PG_RETURN_CASH(result);
+}
/* cash_div_flt8()
* Divide cash by float8.
- *
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
*/
-Cash *
-cash_div_flt8(Cash *c, float8 *f)
+Datum
+cash_div_flt8(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(f) || !PointerIsValid(c))
- return (NULL);
+ Cash c = PG_GETARG_CASH(0);
+ float8 f = PG_GETARG_FLOAT8(1);
+ Cash result;
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
+ if (f == 0.0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
- if (*f == 0.0)
- elog(ERROR, "cash_div: divide by 0.0 error");
+ result = rint(c / f);
+ PG_RETURN_CASH(result);
+}
- *result = rint(*c / *f);
-
- return (result);
-} /* cash_div_flt8() */
/* cash_mul_flt4()
* Multiply cash by float4.
*/
-Cash *
-cash_mul_flt4(Cash *c, float4 *f)
+Datum
+cash_mul_flt4(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(f) || !PointerIsValid(c))
- return (NULL);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
-
- *result = ((*f) * (*c));
+ Cash c = PG_GETARG_CASH(0);
+ float4 f = PG_GETARG_FLOAT4(1);
+ Cash result;
- return (result);
-} /* cash_mul_flt4() */
+ result = c * f;
+ PG_RETURN_CASH(result);
+}
/* flt4_mul_cash()
- * Multiply float4 by float4.
+ * Multiply float4 by cash.
*/
-Cash *
-flt4_mul_cash(float4 *f, Cash *c)
+Datum
+flt4_mul_cash(PG_FUNCTION_ARGS)
{
- return (cash_mul_flt4(c, f));
-} /* flt4_mul_cash() */
+ float4 f = PG_GETARG_FLOAT4(0);
+ Cash c = PG_GETARG_CASH(1);
+ Cash result;
+
+ result = f * c;
+ PG_RETURN_CASH(result);
+}
/* cash_div_flt4()
* Divide cash by float4.
*
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
*/
-Cash *
-cash_div_flt4(Cash *c, float4 *f)
+Datum
+cash_div_flt4(PG_FUNCTION_ARGS)
{
- Cash *result;
+ Cash c = PG_GETARG_CASH(0);
+ float4 f = PG_GETARG_FLOAT4(1);
+ Cash result;
- if (!PointerIsValid(f) || !PointerIsValid(c))
- return (NULL);
+ if (f == 0.0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
+ result = rint(c / f);
+ PG_RETURN_CASH(result);
+}
- if (*f == 0.0)
- elog(ERROR, "cash_div: divide by 0.0 error");
- *result = rint(*c / *f);
+/* cash_mul_int8()
+ * Multiply cash by int8.
+ */
+Datum
+cash_mul_int8(PG_FUNCTION_ARGS)
+{
+ Cash c = PG_GETARG_CASH(0);
+ int64 i = PG_GETARG_INT64(1);
+ Cash result;
- return (result);
-} /* cash_div_flt4() */
+ result = c * i;
+ PG_RETURN_CASH(result);
+}
-/* cash_mul_int4()
- * Multiply cash by int4.
+/* int8_mul_cash()
+ * Multiply int8 by cash.
+ */
+Datum
+int8_mul_cash(PG_FUNCTION_ARGS)
+{
+ int64 i = PG_GETARG_INT64(0);
+ Cash c = PG_GETARG_CASH(1);
+ Cash result;
+
+ result = i * c;
+ PG_RETURN_CASH(result);
+}
+
+/* cash_div_int8()
+ * Divide cash by 8-byte integer.
*/
-Cash *
-cash_mul_int4(Cash *c, int4 i)
+Datum
+cash_div_int8(PG_FUNCTION_ARGS)
{
- Cash *result;
+ Cash c = PG_GETARG_CASH(0);
+ int64 i = PG_GETARG_INT64(1);
+ Cash result;
+
+ if (i == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+
+ result = rint(c / i);
- if (!PointerIsValid(c))
- return (NULL);
+ PG_RETURN_CASH(result);
+}
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
- *result = ((i) * (*c));
+/* cash_mul_int4()
+ * Multiply cash by int4.
+ */
+Datum
+cash_mul_int4(PG_FUNCTION_ARGS)
+{
+ Cash c = PG_GETARG_CASH(0);
+ int32 i = PG_GETARG_INT32(1);
+ Cash result;
- return (result);
-} /* cash_mul_int4() */
+ result = c * i;
+ PG_RETURN_CASH(result);
+}
/* int4_mul_cash()
* Multiply int4 by cash.
*/
-Cash *
-int4_mul_cash(int4 i, Cash *c)
+Datum
+int4_mul_cash(PG_FUNCTION_ARGS)
{
- return (cash_mul_int4(c, i));
-} /* int4_mul_cash() */
+ int32 i = PG_GETARG_INT32(0);
+ Cash c = PG_GETARG_CASH(1);
+ Cash result;
+
+ result = i * c;
+ PG_RETURN_CASH(result);
+}
/* cash_div_int4()
* Divide cash by 4-byte integer.
*
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
*/
-Cash *
-cash_div_int4(Cash *c, int4 i)
+Datum
+cash_div_int4(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(c))
- return (NULL);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
+ Cash c = PG_GETARG_CASH(0);
+ int32 i = PG_GETARG_INT32(1);
+ Cash result;
if (i == 0)
- elog(ERROR, "cash_idiv: divide by 0 error");
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
- *result = rint(*c / i);
+ result = rint(c / i);
- return (result);
-} /* cash_div_int4() */
+ PG_RETURN_CASH(result);
+}
/* cash_mul_int2()
* Multiply cash by int2.
*/
-Cash *
-cash_mul_int2(Cash *c, int2 s)
+Datum
+cash_mul_int2(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(c))
- return (NULL);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
-
- *result = ((s) * (*c));
-
- return (result);
-} /* cash_mul_int2() */
+ Cash c = PG_GETARG_CASH(0);
+ int16 s = PG_GETARG_INT16(1);
+ Cash result;
+ result = c * s;
+ PG_RETURN_CASH(result);
+}
/* int2_mul_cash()
* Multiply int2 by cash.
*/
-Cash *
-int2_mul_cash(int2 s, Cash *c)
+Datum
+int2_mul_cash(PG_FUNCTION_ARGS)
{
- return (cash_mul_int2(c, s));
-} /* int2_mul_cash() */
+ int16 s = PG_GETARG_INT16(0);
+ Cash c = PG_GETARG_CASH(1);
+ Cash result;
+ result = s * c;
+ PG_RETURN_CASH(result);
+}
/* cash_div_int2()
* Divide cash by int2.
*
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
*/
-Cash *
-cash_div_int2(Cash *c, int2 s)
+Datum
+cash_div_int2(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(c))
- return (NULL);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
+ Cash c = PG_GETARG_CASH(0);
+ int16 s = PG_GETARG_INT16(1);
+ Cash result;
if (s == 0)
- elog(ERROR, "cash_div: divide by 0 error");
-
- *result = rint(*c / s);
-
- return (result);
-} /* cash_div_int2() */
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ result = rint(c / s);
+ PG_RETURN_CASH(result);
+}
/* cashlarger()
* Return larger of two cash values.
*/
-Cash *
-cashlarger(Cash *c1, Cash *c2)
+Datum
+cashlarger(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (NULL);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
+ Cash result;
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't return larger cash", NULL);
-
- *result = ((*c1 > *c2) ? *c1 : *c2);
-
- return (result);
-} /* cashlarger() */
+ result = (c1 > c2) ? c1 : c2;
+ PG_RETURN_CASH(result);
+}
/* cashsmaller()
* Return smaller of two cash values.
*/
-Cash *
-cashsmaller(Cash *c1, Cash *c2)
+Datum
+cashsmaller(PG_FUNCTION_ARGS)
{
- Cash *result;
-
- if (!PointerIsValid(c1) || !PointerIsValid(c2))
- return (NULL);
-
- if (!PointerIsValid(result = palloc(sizeof(Cash))))
- elog(ERROR, "Memory allocation failed, can't return smaller cash", NULL);
+ Cash c1 = PG_GETARG_CASH(0);
+ Cash c2 = PG_GETARG_CASH(1);
+ Cash result;
- *result = ((*c1 < *c2) ? *c1 : *c2);
+ result = (c1 < c2) ? c1 : c2;
- return (result);
-} /* cashsmaller() */
+ PG_RETURN_CASH(result);
+}
-
-/* cash_words_out()
- * This converts a int4 as well but to a representation using words
+/* cash_words()
+ * This converts an int4 as well but to a representation using words
* Obviously way North American centric - sorry
*/
-const char *
-cash_words_out(Cash *value)
+Datum
+cash_words(PG_FUNCTION_ARGS)
{
- static char buf[128];
+ Cash value = PG_GETARG_CASH(0);
+ uint64 val;
+ char buf[256];
char *p = buf;
Cash m0;
Cash m1;
Cash m2;
Cash m3;
+ Cash m4;
+ Cash m5;
+ Cash m6;
/* work with positive numbers */
- if (*value < 0)
+ if (value < 0)
{
- *value *= -1;
+ value = -value;
strcpy(buf, "minus ");
p += 6;
}
else
- *buf = 0;
+ buf[0] = '\0';
+
+ /* Now treat as unsigned, to avoid trouble at INT_MIN */
+ val = (uint64) value;
- m0 = *value % 100; /* cents */
- m1 = (*value / 100) % 1000; /* hundreds */
- m2 = (*value / 100000) % 1000; /* thousands */
- m3 = *value / 100000000 % 1000; /* millions */
+ m0 = val % INT64CONST(100); /* cents */
+ m1 = (val / INT64CONST(100)) % 1000; /* hundreds */
+ m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
+ m3 = (val / INT64CONST(100000000)) % 1000; /* millions */
+ m4 = (val / INT64CONST(100000000000)) % 1000; /* billions */
+ m5 = (val / INT64CONST(100000000000000)) % 1000; /* trillions */
+ m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
+
+ if (m6)
+ {
+ strcat(buf, num_word(m6));
+ strcat(buf, " quadrillion ");
+ }
+
+ if (m5)
+ {
+ strcat(buf, num_word(m5));
+ strcat(buf, " trillion ");
+ }
+
+ if (m4)
+ {
+ strcat(buf, num_word(m4));
+ strcat(buf, " billion ");
+ }
if (m3)
{
if (!*p)
strcat(buf, "zero");
- strcat(buf, (int) (*value / 100) == 1 ? " dollar and " : " dollars and ");
+ strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
strcat(buf, num_word(m0));
strcat(buf, m0 == 1 ? " cent" : " cents");
- *buf = toupper(*buf);
- return (buf);
-} /* cash_words_out() */
+ /* capitalize output */
+ buf[0] = pg_toupper((unsigned char) buf[0]);
-/*************************************************************************
- * Private routines
- ************************************************************************/
+ /* return as text datum */
+ PG_RETURN_TEXT_P(cstring_to_text(buf));
+}
-static const char *
-num_word(Cash value)
+
+/* cash_numeric()
+ * Convert cash to numeric.
+ */
+Datum
+cash_numeric(PG_FUNCTION_ARGS)
{
- static char buf[128];
- static const char *small[] = {
- "zero", "one", "two", "three", "four", "five", "six", "seven",
- "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
- "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
- "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"
- };
- const char **big = small + 18;
- int tu = value % 100;
+ Cash money = PG_GETARG_CASH(0);
+ Numeric result;
+ int fpoint;
+ int64 scale;
+ int i;
+ Datum amount;
+ Datum numeric_scale;
+ Datum quotient;
+ struct lconv *lconvert = PGLC_localeconv();
+
+ /* see comments about frac_digits in cash_in() */
+ fpoint = lconvert->frac_digits;
+ if (fpoint < 0 || fpoint > 10)
+ fpoint = 2;
+
+ /* compute required scale factor */
+ scale = 1;
+ for (i = 0; i < fpoint; i++)
+ scale *= 10;
+
+ /* form the result as money / scale */
+ amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
+ numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
+ quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);
+
+ /* forcibly round to exactly the intended number of digits */
+ result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
+ quotient,
+ Int32GetDatum(fpoint)));
+
+ PG_RETURN_NUMERIC(result);
+}
+
+/* numeric_cash()
+ * Convert numeric to cash.
+ */
+Datum
+numeric_cash(PG_FUNCTION_ARGS)
+{
+ Datum amount = PG_GETARG_DATUM(0);
+ Cash result;
+ int fpoint;
+ int64 scale;
+ int i;
+ Datum numeric_scale;
+ struct lconv *lconvert = PGLC_localeconv();
- /* deal with the simple cases first */
- if (value <= 20)
- return (small[value]);
+ /* see comments about frac_digits in cash_in() */
+ fpoint = lconvert->frac_digits;
+ if (fpoint < 0 || fpoint > 10)
+ fpoint = 2;
- /* is it an even multiple of 100? */
- if (!tu)
- {
- sprintf(buf, "%s hundred", small[value / 100]);
- return (buf);
- }
+ /* compute required scale factor */
+ scale = 1;
+ for (i = 0; i < fpoint; i++)
+ scale *= 10;
- /* more than 99? */
- if (value > 99)
- {
- /* is it an even multiple of 10 other than 10? */
- if (value % 10 == 0 && tu > 10)
- sprintf(buf, "%s hundred %s",
- small[value / 100], big[tu / 10]);
- else if (tu < 20)
- sprintf(buf, "%s hundred and %s",
- small[value / 100], small[tu]);
- else
- sprintf(buf, "%s hundred %s %s",
- small[value / 100], big[tu / 10], small[tu % 10]);
+ /* multiply the input amount by scale factor */
+ numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
+ amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
- }
- else
- {
- /* is it an even multiple of 10 other than 10? */
- if (value % 10 == 0 && tu > 10)
- sprintf(buf, "%s", big[tu / 10]);
- else if (tu < 20)
- sprintf(buf, "%s", small[tu]);
- else
- sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
- }
+ /* note that numeric_int8 will round to nearest integer for us */
+ result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
- return (buf);
-} /* num_word() */
+ PG_RETURN_CASH(result);
+}
+
+/* int4_cash()
+ * Convert int4 (int) to cash
+ */
+Datum
+int4_cash(PG_FUNCTION_ARGS)
+{
+ int32 amount = PG_GETARG_INT32(0);
+ Cash result;
+ int fpoint;
+ int64 scale;
+ int i;
+ struct lconv *lconvert = PGLC_localeconv();
+
+ /* see comments about frac_digits in cash_in() */
+ fpoint = lconvert->frac_digits;
+ if (fpoint < 0 || fpoint > 10)
+ fpoint = 2;
+
+ /* compute required scale factor */
+ scale = 1;
+ for (i = 0; i < fpoint; i++)
+ scale *= 10;
+
+ /* compute amount * scale, checking for overflow */
+ result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
+ Int64GetDatum(scale)));
+
+ PG_RETURN_CASH(result);
+}
+
+/* int8_cash()
+ * Convert int8 (bigint) to cash
+ */
+Datum
+int8_cash(PG_FUNCTION_ARGS)
+{
+ int64 amount = PG_GETARG_INT64(0);
+ Cash result;
+ int fpoint;
+ int64 scale;
+ int i;
+ struct lconv *lconvert = PGLC_localeconv();
+
+ /* see comments about frac_digits in cash_in() */
+ fpoint = lconvert->frac_digits;
+ if (fpoint < 0 || fpoint > 10)
+ fpoint = 2;
+
+ /* compute required scale factor */
+ scale = 1;
+ for (i = 0; i < fpoint; i++)
+ scale *= 10;
+
+ /* compute amount * scale, checking for overflow */
+ result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
+ Int64GetDatum(scale)));
+
+ PG_RETURN_CASH(result);
+}