From 88d740462f04f51080b4795d2c6ea2c842820319 Mon Sep 17 00:00:00 2001
From: "Marc G. Fournier" <scrappy@hub.org>
Date: Tue, 15 Apr 1997 17:41:44 +0000
Subject: [PATCH] From: Thomas Lockhart <Thomas.G.Lockhart@jpl.nasa.gov>
 Subject: [HACKERS] Money integration patches

Here are patches to integrate the money data type. I have included
some math and aggregate functions and have made the locale support optional
by #ifdef USE_LOCALE bracketing of functions.

Modules affected are:
builtins.h.patch
cash.c.patch
cash.h.patch
main.c.patch
pg_aggregate.h.patch
pg_operator.h.patch
pg_proc.h.patch
pg_type.h.patch

I changed the data type to be pass-by-reference rather than by-value
to pave the way for a larger internal representation (64-bit ints?).
Also, I changed the tabbing of cash.c and cash.h to match most of
the other Postgres source code files (4 space indent, 8 spaces == 1 tab).

The locale stuff should be tested under another convention (Russian?)
but I don't know what the correct results should be so perhaps someone
else can give them a try. Will update docs and regression tests in
the next few days.
---
 src/backend/main/main.c            |   3 +-
 src/backend/port/Makefile.in       |   3 +-
 src/backend/utils/adt/cash.c       | 729 +++++++++++++++++++----------
 src/include/catalog/pg_aggregate.h |  12 +-
 src/include/catalog/pg_operator.h  |  49 +-
 src/include/catalog/pg_proc.h      |  22 +-
 src/include/catalog/pg_type.h      |  29 +-
 src/include/utils/builtins.h       |   3 +-
 src/include/utils/cash.h           |  41 +-
 9 files changed, 592 insertions(+), 299 deletions(-)

diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index 5803f9dd3ba..7887aff3f1c 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.5 1997/04/12 09:37:31 scrappy Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.6 1997/04/15 17:39:17 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@ main(int argc, char *argv[])
 #ifdef USE_LOCALE
     setlocale(LC_CTYPE,""); /* take locale information from an environment */
     setlocale(LC_COLLATE,"");
+    setlocale(LC_MONETARY,"");
 #endif
 #if defined(NOFIXADE) || defined(NOPRINTADE)
     /*
diff --git a/src/backend/port/Makefile.in b/src/backend/port/Makefile.in
index a2d23188cef..e7e7e49a413 100644
--- a/src/backend/port/Makefile.in
+++ b/src/backend/port/Makefile.in
@@ -19,10 +19,11 @@
 # be converted to Method 2.  
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/port/Attic/Makefile.in,v 1.3 1997/03/19 02:33:29 scrappy Exp $
+#    $Header: /cvsroot/pgsql/src/backend/port/Attic/Makefile.in,v 1.4 1997/04/15 17:39:23 scrappy Exp $
 #
 #-------------------------------------------------------------------------
 
+SRCDIR=../..
 include ../../Makefile.global
 
 ifndef PORTNAME
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index 5ef92c295b4..7f6323a7ae7 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -1,274 +1,521 @@
 /*
-cash.c
-Written by D'Arcy J.M. Cain
+ * cash.c
+ * Written by D'Arcy J.M. Cain
+ *
+ * Functions to allow input and output of money normally but store
+ * and handle it as longs
+ *
+ * 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.
+ * 
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.4 1997/04/15 17:39:44 scrappy Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/cash.h"
 
-Functions to allow input and output of money normally but store
-and handle it as longs
-
-Set tabstops to 4 for best results
+/* when we go to 64 bit values we will have to modify this */
+#define CASH_BUFSZ	24
+
+#define TERMINATOR	(CASH_BUFSZ - 1)
+#define LAST_PAREN	(TERMINATOR - 1)
+#define LAST_DIGIT	(LAST_PAREN - 1)
+
+#ifdef USE_LOCALE
+static struct lconv *lconv = NULL;
+#endif
+
+/* 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
+ */
+Cash *
+cash_in(const char *str)
+{
+    Cash *result;
+
+    Cash value = 0;
+    Cash dec = 0;
+    Cash sgn = 1;
+    int seen_dot = 0;
+    const char *s = str;
+    int fpoint;
+    char dsymbol, ssymbol, psymbol, nsymbol, csymbol;
+
+#ifdef USE_LOCALE
+    if (lconv == NULL) *lconv = localeconv();
+
+    /* frac_digits in the C locale seems to return CHAR_MAX */
+    /* best guess is 2 in this case I think */
+    fpoint = ((lconv->frac_digits != CHAR_MAX)? lconv->frac_digits: 2); /* int_frac_digits? */
+
+    dsymbol = *lconv->mon_decimal_point;
+    ssymbol = *lconv->mon_thousands_sep;
+    csymbol = *lconv->currency_symbol;
+    psymbol = *lconv->positive_sign;
+    nsymbol = *lconv->negative_sign;
+#else
+    fpoint = 2;
+    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 dollar sign */
+    while (isspace(*s) || *s == csymbol) s++;
+
+    /* a leading minus or paren signifies a negative number */
+    /* again, better heuristics needed */
+    if (*s == nsymbol || *s == '(') {
+	sgn = -1;
+	s++;
+
+    } else if (*s == psymbol) {
+	s++;
+    }
+
+    while (isspace(*s) || *s == csymbol) s++;
+
+    for (; ; s++) {
+	/* we look for digits as long as we have less */
+	/* than the required number of decimal places */
+	if (isdigit(*s) && dec < fpoint) {
+	    value = (value * 10) + *s - '0';
+
+	    if (seen_dot)
+		dec++;
+
+	/* decimal point? then start counting fractions... */
+	} else if (*s == dsymbol && !seen_dot) {
+	    seen_dot = 1;
+
+	/* "thousands" separator? then skip... */
+	} else if (*s == ssymbol) {
+
+	} else {
+	    /* round off */
+	    if (isdigit(*s) && *s >= '5')
+		value++;
+
+	    /* adjust for less than required decimal places */
+	    for (; dec < fpoint; dec++)
+		value *= 10;
+
+	    break;
+	}
+    }
 
-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.
+    while (isspace(*s) || *s == ')') s++;
 
-	$Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.3 1997/04/10 20:51:13 scrappy Exp $
-*/
+    if (*s != '\0')
+	elog(WARN,"Bad money external representation %s",str);
 
-#include	<stdio.h>
-#include	<string.h>
-#include	<limits.h>
-#include	<ctype.h>
-#include	<locale.h>
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't input cash '%s'",str);
 
-#include	<utils/cash.h>
+    *result = (value * sgn);
 
-/* when we go to 64 bit values we will have to modify this */
-#define		CASH_BUFSZ	24
+    return(result);
+} /* cash_in() */
 
-#define		TERMINATOR	(CASH_BUFSZ - 1)
-#define		LAST_PAREN	(TERMINATOR - 1)
-#define		LAST_DIGIT	(LAST_PAREN - 1)
 
-/* function to convert a long to a dollars and cents representation */
+/* 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
+ */
 const char *
-cash_out(long value)
+cash_out(Cash *value)
 {
-	char			*retbuf, buf[CASH_BUFSZ];
-	struct lconv	*lc = localeconv();
-	int				mod_group = *lc->mon_grouping;
-	int				comma = *lc->mon_thousands_sep;
-	int				points = lc->frac_digits;		/* int_frac_digits? */
-	int				minus = 0;
-	int				count = LAST_DIGIT;
-	int				point_pos;
-	int				comma_position = 0;
-
-	/* frac_digits in the C locale seems to return CHAR_MAX */
-	/* best guess is 2 in this case I think */
-	if (points == CHAR_MAX)
-		points = 2;
-
-	point_pos = LAST_DIGIT - points;
-
-	/* We're playing a little fast and loose with this.  Shoot me. */
-	if (!mod_group || mod_group == CHAR_MAX)
-		mod_group = 3;
-
-	/* allow more than three decimal points and separate them */
-	if (comma)
-	{
-		point_pos -= (points - 1)/mod_group;
-		comma_position = point_pos % (mod_group + 1);
-	}
+    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 (lconv == NULL) *lconv = localeconv();
+
+    mon_group = *lconv->mon_grouping;
+    comma = *lconv->mon_thousands_sep;
+    csymbol = *lconv->currency_symbol;
+    dsymbol = *lconv->mon_decimal_point;
+    nsymbol = lconv->negative_sign;
+    /* frac_digits in the C locale seems to return CHAR_MAX */
+    /* best guess is 2 in this case I think */
+    points = ((lconv->frac_digits != CHAR_MAX)? lconv->frac_digits: 2); /* int_frac_digits? */
+    convention = lconv->n_sign_posn;
+#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. */
+    if (!mon_group || mon_group == CHAR_MAX)
+	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 work with positive amounts and add the minus sign at the end */
+    if (*value < 0) {
+	minus = 1;
+	*value *= -1;
+    }
+
+    /* allow for trailing negative strings */
+    memset(buf, ' ', CASH_BUFSZ);
+    buf[TERMINATOR] = buf[LAST_PAREN] = '\0';
+
+    while (*value || count > (point_pos - 2)) {
+	if (points && count == point_pos)
+	    buf[count--] = dsymbol;
+	else if (comma && count % (mon_group + 1) == comma_position)
+	    buf[count--] = comma;
+
+	buf[count--] = (*value % 10) + '0';
+	*value /= 10;
+    }
+
+    buf[count] = csymbol;
+
+    if (buf[LAST_DIGIT] == ',')
+	buf[LAST_DIGIT] = buf[LAST_PAREN];
+
+    /* see if we need to signify negative amount */
+    if (minus) {
+	if (!PointerIsValid(result = PALLOC(CASH_BUFSZ + 2 - count + strlen(nsymbol))))
+	    elog(WARN,"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(WARN,"Memory allocation failed, can't output cash",NULL);
 
-	/* we work with positive amounts and add the minus sign at the end */
-	if (value < 0)
-	{
-		minus = 1;
-		value *= -1;
-	}
+	strcpy(result, buf + count);
+    }
 
-	/* allow for trailing negative strings */
-	memset(buf, ' ', CASH_BUFSZ);
-	buf[TERMINATOR] = buf[LAST_PAREN] = 0;
+    return(result);
+} /* cash_out() */
 
-	while (value || count > (point_pos - 2))
-	{
-		if (points && count == point_pos)
-			buf[count--] = *lc->decimal_point;
-		else if (comma && count % (mod_group + 1) == comma_position)
-			buf[count--] = comma;
 
-		buf[count--] = (value % 10) + '0';
-		value /= 10;
-	}
+bool
+cash_eq(Cash *c1, Cash *c2)
+{
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(FALSE);
 
-	if (buf[LAST_DIGIT] == ',')
-		buf[LAST_DIGIT] = buf[LAST_PAREN];
-
-	/* see if we need to signify negative amount */
-	if (minus)
-	{
-		retbuf = palloc(CASH_BUFSZ + 2 - count + strlen(lc->negative_sign));
-
-		/* Position code of 0 means use parens */
-		if (!lc->n_sign_posn)
-			sprintf(retbuf, "(%s)", buf + count);
-		else if (lc->n_sign_posn == 2)
-			sprintf(retbuf, "%s%s", buf + count, lc->negative_sign);
-		else
-			sprintf(retbuf, "%s%s", lc->negative_sign, buf + count);
-	}
-	else
-	{
-		retbuf = palloc(CASH_BUFSZ + 2 - count);
-		strcpy(retbuf, buf + count);
-	}
+    return(*c1 == *c2);
+} /* cash_eq() */
+
+bool
+cash_ne(Cash *c1, Cash *c2)
+{
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(FALSE);
 
-	return retbuf;
-}
+    return(*c1 != *c2);
+} /* cash_ne() */
 
-/* convert a string to a long integer */
-long
-cash_in(const char *s)
+bool
+cash_lt(Cash *c1, Cash *c2)
 {
-	long			value = 0;
-	long			dec = 0;
-	long			sgn = 1;
-	int				seen_dot = 0;
-	struct lconv	*lc = localeconv();
-	int				fpoint = lc->frac_digits;		/* int_frac_digits? */
-
-	/* we need to add all sorts of checking here.  For now just */
-	/* strip all leading whitespace and any leading dollar sign */
-	while (isspace(*s) || *s == '$')
-		s++;
-
-	/* a leading minus or paren signifies a negative number */
-	/* again, better heuristics needed */
-	if (*s == '-' || *s == '(')
-	{
-		sgn = -1;
-		s++;
-	}
-	else if (*s == '+')
-		s++;
-
-	/* frac_digits in the C locale seems to return CHAR_MAX */
-	/* best guess is 2 in this case I think */
-	if (fpoint == CHAR_MAX)
-		fpoint = 2;
-
-	for (; ; s++)
-	{
-		/* we look for digits as long as we have less */
-		/* than the required number of decimal places */
-		if (isdigit(*s) && dec < fpoint)
-		{
-			value = (value * 10) + *s - '0';
-
-			if (seen_dot)
-				dec++;
-		}
-		else if (*s == *lc->decimal_point && !seen_dot)
-			seen_dot = 1;
-		else
-		{
-			/* round off */
-			if (isdigit(*s) && *s >= '5')
-				value++;
-
-			/* adjust for less than required decimal places */
-			for (; dec < fpoint; dec++)
-				value *= 10;
-
-			return(value * sgn);
-		}
-	}
-}
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(FALSE);
 
+    return(*c1 < *c2);
+} /* cash_lt() */
 
-/* used by cash_words_out() below */
-static const char *
-num_word(int value)
+bool
+cash_le(Cash *c1, Cash *c2)
 {
-	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;
-
-	/* deal with the simple cases first */
-	if (value <= 20)
-		return(small[value]);
-
-	/* is it an even multiple of 100? */
-	if (!tu)
-	{
-		sprintf(buf, "%s hundred", small[value/100]);
-		return(buf);
-	}
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(FALSE);
 
-	/* 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(*c1 <= *c2);
+} /* cash_le() */
 
-	return(buf);
-}
+bool
+cash_gt(Cash *c1, Cash *c2)
+{
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(FALSE);
 
-/* this converts a long as well but to a representation using words */
-/* obviously way North American centric - sorry */
-const char *
-cash_words_out(long value)
+    return(*c1 > *c2);
+} /* cash_gt() */
+
+bool
+cash_ge(Cash *c1, Cash *c2)
 {
-	static char	buf[128];
-	char	*p = buf;
-	long	m0;
-	long	m1;
-	long	m2;
-	long	m3;
-
-	/* work with positive numbers */
-	if (value < 0)
-	{
-		value *= -1;
-		strcpy(buf, "minus ");
-		p += 6;
-	}
-	else
-		*buf = 0;
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(FALSE);
 
-	m0 = value % 100;				/* cents */
-	m1 = (value/100) % 1000;		/* hundreds */
-	m2 = (value/100000) % 1000;		/* thousands */
-	m3 = value/100000000 % 1000;	/* millions */
+    return(*c1 >= *c2);
+} /* cash_ge() */
 
-	if (m3)
-	{
-		strcat(buf, num_word(m3));
-		strcat(buf, " million ");
-	}
 
-	if (m2)
-	{
-		strcat(buf, num_word(m2));
-		strcat(buf, " thousand ");
-	}
+/* cash_pl()
+ * Add two cash values.
+ */
+Cash *
+cash_pl(Cash *c1, Cash *c2)
+{
+    Cash *result;
+
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(NULL);
+
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't add cash",NULL);
+
+    *result = (*c1 + *c2);
+
+    return(result);
+} /* cash_pl() */
+
+
+/* cash_mi()
+ * Subtract two cash values.
+ */
+Cash *
+cash_mi(Cash *c1, Cash *c2)
+{
+    Cash *result;
+
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(NULL);
 
-	if (m1)
-		strcat(buf, num_word(m1));
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't subtract cash",NULL);
 
-	if (!*p)
-		strcat(buf, "zero");
+    *result = (*c1 - *c2);
 
-	strcat(buf, (int)(value/100) == 1 ? " dollar and " : " dollars and ");
-	strcat(buf, num_word(m0));
-	strcat(buf, m0 == 1 ? " cent" : " cents");
-	*buf = toupper(*buf);
+    return(result);
+} /* cash_mi() */
+
+
+/* cash_mul()
+ * Multiply cash by floating point number.
+ */
+Cash *
+cash_mul(Cash *c, float8 *f)
+{
+    Cash *result;
+
+    if (!PointerIsValid(f) || !PointerIsValid(c))
+	return(NULL);
+
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't multiply cash",NULL);
+
+    *result = ((*f) * (*c));
+
+    return(result);
+} /* cash_mul() */
+
+
+/* cash_div()
+ * Divide cash by floating point number.
+ *
+ * XXX Don't know if rounding or truncating is correct behavior.
+ * Round for now. - tgl 97/04/15
+ */
+Cash *
+cash_div(Cash *c, float8 *f)
+{
+    Cash *result;
+
+    if (!PointerIsValid(f) || !PointerIsValid(c))
+	return(NULL);
+
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't divide cash",NULL);
+
+    *result = rint(*c / *f);
+
+    return(result);
+} /* cash_div() */
+
+
+/* cashlarger()
+ * Return larger of two cash values.
+ */
+Cash *
+cashlarger(Cash *c1, Cash *c2)
+{
+    Cash *result;
+
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(NULL);
+
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't return larger cash",NULL);
+
+    *result = ((*c1 > *c2)? *c1: *c2);
+
+    return(result);
+} /* cashlarger() */
+
+
+/* cashsmaller()
+ * Return smaller of two cash values.
+ */
+Cash *
+cashsmaller(Cash *c1, Cash *c2)
+{
+    Cash *result;
+
+    if (!PointerIsValid(c1) || !PointerIsValid(c2))
+	return(NULL);
+
+    if (!PointerIsValid(result = PALLOCTYPE(Cash)))
+        elog(WARN,"Memory allocation failed, can't return smaller cash",NULL);
+
+    *result = ((*c1 < *c2)? *c1: *c2);
+
+    return(result);
+} /* cashsmaller() */
+
+
+/* cash_words_out()
+ * This converts a long as well but to a representation using words
+ * Obviously way North American centric - sorry
+ */
+const char *
+cash_words_out(Cash *value)
+{
+    static char	buf[128];
+    char *p = buf;
+    Cash m0;
+    Cash m1;
+    Cash m2;
+    Cash m3;
+
+    /* work with positive numbers */
+    if (*value < 0) {
+	*value *= -1;
+	strcpy(buf, "minus ");
+	p += 6;
+    } else {
+	*buf = 0;
+    }
+
+    m0 = *value % 100; /* cents */
+    m1 = (*value/100) % 1000; /* hundreds */
+    m2 = (*value/100000) % 1000; /* thousands */
+    m3 = *value/100000000 % 1000; /* millions */
+
+    if (m3) {
+	strcat(buf, num_word(m3));
+	strcat(buf, " million ");
+    }
+
+    if (m2) {
+	strcat(buf, num_word(m2));
+	strcat(buf, " thousand ");
+    }
+
+    if (m1)
+	strcat(buf, num_word(m1));
+
+    if (!*p)
+	strcat(buf, "zero");
+
+    strcat(buf, (int)(*value/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() */
+
+
+/*************************************************************************
+ * Private routines
+ ************************************************************************/
+
+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", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"
+    };
+    const char **big = small + 18;
+    int tu = value % 100;
+
+    /* deal with the simple cases first */
+    if (value <= 20)
+	return(small[value]);
+
+    /* is it an even multiple of 100? */
+    if (!tu) {
+	sprintf(buf, "%s hundred", small[value/100]);
 	return(buf);
-}
+    }
+
+    /* 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() */
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index dcf7a79df63..69eb5d186ed 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_aggregate.h,v 1.5 1997/04/03 19:56:19 scrappy Exp $
+ * $Id: pg_aggregate.h,v 1.6 1997/04/15 17:40:19 scrappy Exp $
  *
  * NOTES
  *    the genbki.sh script reads this file and generates .bki
@@ -35,9 +35,9 @@
  *  aggtransfn2		transition function 2
  *  aggfinalfn		final function
  *  aggbasetype		type of data on which aggregate operates
- *  aggtranstype1	output types for xition func 1
- *  aggtranstype2	output types for xition func 2
- *  aggfinaltype	output type for final func
+ *  aggtranstype1	output types for transition func 1
+ *  aggtranstype2	output types for transition func 2
+ *  aggfinaltype	output type for final function
  *  agginitval1		initial aggregate value
  *  agginitval2		initial value for transition state 2
  * ----------------------------------------------------------------
@@ -91,11 +91,13 @@ DATA(insert OID = 0 ( avg   PGUID int4pl   int4inc   int4div     23   23   23
 DATA(insert OID = 0 ( avg   PGUID int2pl   int2inc   int2div     21   21   21   21 _null_ 0 ));
 DATA(insert OID = 0 ( avg   PGUID float4pl float4inc float4div  700  700  700  700 _null_ 0.0 ));
 DATA(insert OID = 0 ( avg   PGUID float8pl float8inc float8div  701  701  701  701 _null_ 0.0 ));
+DATA(insert OID = 0 ( avg   PGUID cash_pl  float8inc cash_div   790  790  701  790 _null_ 0.0 ));
 
 DATA(insert OID = 0 ( sum   PGUID int4pl        - -   23   23 0   23 _null_ _null_ ));
 DATA(insert OID = 0 ( sum   PGUID int2pl        - -   21   21 0   21 _null_ _null_ ));
 DATA(insert OID = 0 ( sum   PGUID float4pl      - -  700  700 0  700 _null_ _null_ ));
 DATA(insert OID = 0 ( sum   PGUID float8pl      - -  701  701 0  701 _null_ _null_ ));
+DATA(insert OID = 0 ( sum   PGUID cash_pl       - -  790  790 0  790 _null_ _null_ ));
 
 DATA(insert OID = 0 ( max   PGUID int4larger    - -   23   23 0   23 _null_ _null_ ));
 DATA(insert OID = 0 ( max   PGUID int2larger    - -   21   21 0   21 _null_ _null_ ));
@@ -104,6 +106,7 @@ DATA(insert OID = 0 ( max   PGUID float8larger  - -  701  701 0  701 _null_ _nul
 DATA(insert OID = 0 ( max   PGUID int4larger    - -  702  702 0  702 _null_ _null_ ));
 DATA(insert OID = 0 ( max   PGUID date_larger   - - 1082 1082 0 1082 _null_ _null_ ));
 DATA(insert OID = 0 ( max   PGUID float8larger  - - 1084 1084 0 1084 _null_ _null_ ));
+DATA(insert OID = 0 ( max   PGUID cashlarger    - -  790  790 0  790 _null_ _null_ ));
 
 DATA(insert OID = 0 ( min   PGUID int4smaller   - -   23   23 0   23 _null_ _null_ ));
 DATA(insert OID = 0 ( min   PGUID int2smaller   - -   21   21 0   21 _null_ _null_ ));
@@ -112,6 +115,7 @@ DATA(insert OID = 0 ( min   PGUID float8smaller - -  701  701 0  701 _null_ _nul
 DATA(insert OID = 0 ( min   PGUID int4smaller   - -  702  702 0  702 _null_ _null_ ));
 DATA(insert OID = 0 ( min   PGUID date_smaller  - - 1082 1082 0 1082 _null_ _null_ ));
 DATA(insert OID = 0 ( min   PGUID float8smaller - - 1084 1084 0 1084 _null_ _null_ ));
+DATA(insert OID = 0 ( min   PGUID cashsmaller   - -  790  790 0  790 _null_ _null_ ));
 
 DATA(insert OID = 0 ( count PGUID - int4inc - 0 0 23 23 _null_ 0 ));
 
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 04e826b0f2c..fa8a4806873 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.7 1997/04/02 18:36:12 scrappy Exp $
+ * $Id: pg_operator.h,v 1.8 1997/04/15 17:40:44 scrappy Exp $
  *
  * NOTES
  *    the genbki.sh script reads this file and generates .bki
@@ -353,6 +353,17 @@ DATA(insert OID = 841 (  "!~"      PGUID 0 b t f  409  25  16 0 839  0 0 char2re
 DATA(insert OID = 840 (  "~"       PGUID 0 b t f  410  25  16 0 842  0 0 char4regexeq eqsel eqjoinsel ));
 DATA(insert OID = 842 (  "!~"      PGUID 0 b t f  410  25  16 0 840  0 0 char4regexne neqsel neqjoinsel ));
 
+DATA(insert OID = 900 (  "="       PGUID 0 b t t  790  790  16 900 901  902 902 cash_eq eqsel eqjoinsel ));
+DATA(insert OID = 901 (  "<>"      PGUID 0 b t f  790  790  16 901 900  0 0 cash_ne neqsel neqjoinsel ));
+DATA(insert OID = 902 (  "<"       PGUID 0 b t f  790  790  16 903 905  0 0 cash_lt intltsel intltjoinsel ));
+DATA(insert OID = 903 (  ">"       PGUID 0 b t f  790  790  16 902 904  0 0 cash_gt intgtsel intgtjoinsel ));
+DATA(insert OID = 904 (  "<="      PGUID 0 b t f  790  790  16 905 903  0 0 cash_le intltsel intltjoinsel ));
+DATA(insert OID = 905 (  ">="      PGUID 0 b t f  790  790  16 904 902  0 0 cash_ge intgtsel intgtjoinsel ));
+DATA(insert OID = 906 (  "+"       PGUID 0 b t f  790  790  790 906   0   0   0 cash_pl - - ));
+DATA(insert OID = 907 (  "-"       PGUID 0 b t f  790  790  790   0   0   0   0 cash_mi - - ));
+DATA(insert OID = 908 (  "*"       PGUID 0 b t f  790  701  790 909   0   0   0 cash_mul - - ));
+DATA(insert OID = 909 (  "/"       PGUID 0 b t f  790  701  790   0   0   0   0 cash_div - - ));
+
 DATA(insert OID = 930 (  "<"       PGUID 0 b t f  910  910  16 934 933  0 0 oidint4lt intltsel intltjoinsel ));
 DATA(insert OID = 931 (  "<="      PGUID 0 b t f  910  910  16 933 934  0 0 oidint4le intltsel intltjoinsel ));
 DATA(insert OID = 932 (  "="       PGUID 0 b t f  910  910  16 932 935  0 0 oidint4eq intltsel intltjoinsel ));
@@ -360,28 +371,28 @@ DATA(insert OID = 933 (  ">="      PGUID 0 b t f  910  910  16 931 930  0 0 oidi
 DATA(insert OID = 934 (  ">"       PGUID 0 b t f  910  910  16 930 931  0 0 oidint4gt intltsel intltjoinsel ));
 DATA(insert OID = 935 (  "<>"      PGUID 0 b t f  910  910  16 935 932  0 0 oidint4ne intltsel intltjoinsel ));
 
-DATA(insert OID = 965 (  "^"       PGUID 0 b t f 701 701 701   0   0   0   0 dpow - - ));
+DATA(insert OID = 965 (  "^"       PGUID 0 b t f  701  701  701 0 0 0 0 dpow - - ));
 DATA(insert OID = 966 (  "+"       PGUID 0 b t f 1034 1033 1034 0 0 0 0 aclinsert   intltsel intltjoinsel ));
 DATA(insert OID = 967 (  "-"       PGUID 0 b t f 1034 1033 1034 0 0 0 0 aclremove   intltsel intltjoinsel ));
 DATA(insert OID = 968 (  "~"       PGUID 0 b t f 1034 1033   16 0 0 0 0 aclcontains intltsel intltjoinsel ));
 
-DATA(insert OID = 1054 ( "="       PGUID 0 b t t  1042  1042  16  1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
-DATA(insert OID = 1055 (  "~"      PGUID 0 b t f  1042  25  16 0 1056  0 0 textregexeq eqsel eqjoinsel ));
-DATA(insert OID = 1056 ( "!~"      PGUID 0 b t f  1042  25  16 0 1055  0 0 textregexne neqsel neqjoinsel ));
-DATA(insert OID = 1057 ( "<>"      PGUID 0 b t f  1042  1042  16 1057 1054  0 0 bpcharne neqsel neqjoinsel ));
-DATA(insert OID = 1058 ( "<"       PGUID 0 b t f  1042  1042  16 1060 1061  0 0 bpcharlt intltsel intltjoinsel ));
-DATA(insert OID = 1059 ( "<="      PGUID 0 b t f  1042  1042  16 1061 1060  0 0 bpcharle intltsel intltjoinsel ));
-DATA(insert OID = 1060 ( ">"       PGUID 0 b t f  1042  1042  16 1058 1059  0 0 bpchargt intltsel intltjoinsel ));
-DATA(insert OID = 1061 ( ">="      PGUID 0 b t f  1042  1042  16 1059 1058  0 0 bpcharge intltsel intltjoinsel ));
-
-DATA(insert OID = 1062 ( "="       PGUID 0 b t t  1043  1043  16  1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
-DATA(insert OID = 1063 (  "~"      PGUID 0 b t f  1043  25  16 0 1064  0 0 textregexeq eqsel eqjoinsel ));
-DATA(insert OID = 1064 ( "!~"      PGUID 0 b t f  1043  25  16 0 1063  0 0 textregexne neqsel neqjoinsel ));
-DATA(insert OID = 1065 ( "<>"      PGUID 0 b t f  1043  1043  16 1065 1062  0 0 varcharne neqsel neqjoinsel ));
-DATA(insert OID = 1066 ( "<"       PGUID 0 b t f  1043  1043  16 1068 1069  0 0 varcharlt intltsel intltjoinsel ));
-DATA(insert OID = 1067 ( "<="      PGUID 0 b t f  1043  1043  16 1069 1068  0 0 varcharle intltsel intltjoinsel ));
-DATA(insert OID = 1068 ( ">"       PGUID 0 b t f  1043  1043  16 1066 1067  0 0 varchargt intltsel intltjoinsel ));
-DATA(insert OID = 1069 ( ">="      PGUID 0 b t f  1043  1043  16 1067 1066  0 0 varcharge intltsel intltjoinsel ));
+DATA(insert OID = 1054 ( "="       PGUID 0 b t t 1042 1042   16 1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
+DATA(insert OID = 1055 (  "~"      PGUID 0 b t f 1042   25   16    0 1056  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1056 ( "!~"      PGUID 0 b t f 1042   25   16    0 1055  0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 1057 ( "<>"      PGUID 0 b t f 1042 1042   16 1057 1054  0 0 bpcharne neqsel neqjoinsel ));
+DATA(insert OID = 1058 ( "<"       PGUID 0 b t f 1042 1042   16 1060 1061  0 0 bpcharlt intltsel intltjoinsel ));
+DATA(insert OID = 1059 ( "<="      PGUID 0 b t f 1042 1042   16 1061 1060  0 0 bpcharle intltsel intltjoinsel ));
+DATA(insert OID = 1060 ( ">"       PGUID 0 b t f 1042 1042   16 1058 1059  0 0 bpchargt intltsel intltjoinsel ));
+DATA(insert OID = 1061 ( ">="      PGUID 0 b t f 1042 1042   16 1059 1058  0 0 bpcharge intltsel intltjoinsel ));
+
+DATA(insert OID = 1062 ( "="       PGUID 0 b t t 1043 1043  16  1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
+DATA(insert OID = 1063 (  "~"      PGUID 0 b t f 1043   25  16 0 1064  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1064 ( "!~"      PGUID 0 b t f 1043   25  16 0 1063  0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 1065 ( "<>"      PGUID 0 b t f 1043 1043  16 1065 1062  0 0 varcharne neqsel neqjoinsel ));
+DATA(insert OID = 1066 ( "<"       PGUID 0 b t f 1043 1043  16 1068 1069  0 0 varcharlt intltsel intltjoinsel ));
+DATA(insert OID = 1067 ( "<="      PGUID 0 b t f 1043 1043  16 1069 1068  0 0 varcharle intltsel intltjoinsel ));
+DATA(insert OID = 1068 ( ">"       PGUID 0 b t f 1043 1043  16 1066 1067  0 0 varchargt intltsel intltjoinsel ));
+DATA(insert OID = 1069 ( ">="      PGUID 0 b t f 1043 1043  16 1067 1066  0 0 varcharge intltsel intltjoinsel ));
 
 /* date operators */
 DATA(insert OID = 1093 ( "="       PGUID 0 b t t  1082  1082   16 1093 1094 1095 1095 date_eq eqsel eqjoinsel ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e8db3a263eb..bd1cf4f208b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.16 1997/04/02 18:36:24 scrappy Exp $
+ * $Id: pg_proc.h,v 1.17 1997/04/15 17:41:03 scrappy Exp $
  *
  * NOTES
  *    The script catalog/genbki.sh reads this file and generates .bki
@@ -618,6 +618,21 @@ DATA(insert OID =  859 (  namenlike        PGUID 11 f t f 2 f 16 "19 25" 100 0 0
 DATA(insert OID =  860 (  char16like       PGUID 11 f t f 2 f 16 "20 25" 100 0 0 100  foo bar ));
 DATA(insert OID =  861 (  char16nlike      PGUID 11 f t f 2 f 16 "20 25" 100 0 0 100  foo bar ));
  
+DATA(insert OID =  886 (  cash_in          PGUID 11 f t f 1 f 790 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  887 (  cash_out         PGUID 11 f t f 1 f  23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  888 (  cash_eq          PGUID 11 f t f 2 f  16 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  889 (  cash_ne          PGUID 11 f t f 2 f  16 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  890 (  cash_lt          PGUID 11 f t f 2 f  16 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  891 (  cash_le          PGUID 11 f t f 2 f  16 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  892 (  cash_gt          PGUID 11 f t f 2 f  16 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  893 (  cash_ge          PGUID 11 f t f 2 f  16 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  894 (  cash_pl          PGUID 11 f t f 2 f 790 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  895 (  cash_mi          PGUID 11 f t f 2 f 790 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  896 (  cash_mul         PGUID 11 f t f 2 f 790 "790 701" 100 0 0 100  foo bar ));
+DATA(insert OID =  897 (  cash_div         PGUID 11 f t f 2 f 790 "790 701" 100 0 0 100  foo bar ));
+DATA(insert OID =  898 (  cashlarger       PGUID 11 f t f 2 f 790 "790 790" 100 0 0 100  foo bar ));
+DATA(insert OID =  899 (  cashsmaller      PGUID 11 f t f 2 f 790 "790 790" 100 0 0 100  foo bar ));
+
 /* OIDS 900 - 999 */
 
 DATA(insert OID = 920 (  oidint4in	   PGUID 11 f t f 1 f 910 "0" 100 0 0 100  foo bar));
@@ -790,6 +805,7 @@ DATA(insert OID = 1193 (  timespan_text      PGUID 11 f t f 1 f 1186 "25" 100 0
 DATA(insert OID = 1194 (  timespan_reltime   PGUID 11 f t f 1 f  703 "1186" 100 0 0 100  foo bar ));
 /* reserve OIDs 1195-1199 for additional date/time conversion routines! tgl 97/03/19 */
 
+/* OIDS 1200 - 1299 */
 DATA(insert OID = 1200 (  int42reltime     PGUID 11 f t f 1 f 703 "21" 100 0 0 100  foo bar ));
 
 DATA(insert OID = 1290 (  char2icregexeq   PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
@@ -808,6 +824,8 @@ DATA(insert OID = 1241 (  nameicregexne    PGUID 11 f t f 2 f 16 "19 25" 100 0 0
 DATA(insert OID = 1297 (  timestamp_in     PGUID 11 f t f 1 f 1296 "0" 100 0 0 100  foo bar ));
 DATA(insert OID = 1298 (  timestamp_out    PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
 DATA(insert OID = 1299 (  now              PGUID 11 f t f 0 f 1296 "0" 100 0 0 100  foo bar ));
+
+/* OIDS 1300 - 1399 */
 DATA(insert OID = 1306 (  timestampeq      PGUID 11 f t f 2 f 16 "1296 1296" 100 0 0 100  foo bar ));
 DATA(insert OID = 1307 (  timestampne      PGUID 11 f t f 2 f 16 "1296 1296" 100 0 0 100  foo bar ));
 DATA(insert OID = 1308 (  timestamplt      PGUID 11 f t f 2 f 16 "1296 1296" 100 0 0 100  foo bar ));
@@ -854,6 +872,7 @@ DATA(insert OID = 1391 (  isfinite     PGUID 14 f t f 1 f   16 "1186" 100 0 0 10
 DATA(insert OID = 1392 (  isfinite     PGUID 14 f t f 1 f   16  "702" 100 0 0 100  "select abstime_finite($1)" - ));
 /* reserve OIDs 1370-1399 for additional date/time conversion routines! tgl 97/04/01 */
 
+/* OIDS 1400 - 1499 */
 DATA(insert OID = 1400 (  float        PGUID 14 f t f 1 f  701  "701" 100 0 0 100  "select $1" - ));
 DATA(insert OID = 1401 (  float        PGUID 14 f t f 1 f  701  "700" 100 0 0 100  "select ftod($1)" - ));
 DATA(insert OID = 1402 (  float4       PGUID 14 f t f 1 f  700  "700" 100 0 0 100  "select $1" - ));
@@ -861,7 +880,6 @@ DATA(insert OID = 1403 (  float4       PGUID 14 f t f 1 f  700  "701" 100 0 0 10
 DATA(insert OID = 1404 (  int          PGUID 14 f t f 1 f   23   "23" 100 0 0 100  "select $1" - ));
 DATA(insert OID = 1405 (  int2         PGUID 14 f t f 1 f   21   "21" 100 0 0 100  "select $1" - ));
 
-/* reserve OIDs 1370-1399 for additional date/time conversion routines! tgl 97/04/01 */
 /* Oracle Compatibility Related Functions - By Edmund Mergl <E.Mergl@bawue.de> */
 DATA(insert OID =  870 (  lower        PGUID 11 f t f 1 f 25 "25" 100 0 0 100  foo bar ));
 DATA(insert OID =  871 (  upper        PGUID 11 f t f 1 f 25 "25" 100 0 0 100  foo bar ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ad400ed8db9..f5a59fffd42 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_type.h,v 1.9 1997/03/25 08:11:01 scrappy Exp $
+ * $Id: pg_type.h,v 1.10 1997/04/15 17:41:19 scrappy Exp $
  *
  * NOTES
  *    the genbki.sh script reads this file and generates .bki
@@ -143,24 +143,20 @@ typedef TypeTupleFormData	*TypeTupleForm;
 
 /* OIDS 1 - 99 */
 DATA(insert OID = 16 (  bool       PGUID  1   1 t b t \054 0   0 boolin boolout boolin boolout c _null_ ));
-
 #define BOOLOID		16
 
 DATA(insert OID = 17 (  bytea      PGUID -1  -1 f b t \054 0  18 byteain byteaout byteain byteaout i _null_ ));
 DATA(insert OID = 18 (  char       PGUID  1   1 t b t \054 0   0 charin charout charin charout c _null_ ));
 #define CHAROID 18
 
-DATA(insert OID = 19 (  name      PGUID NAMEDATALEN NAMEDATALEN  f b t \054 0  18 namein nameout namein nameout d _null_ ));
+DATA(insert OID = 19 (  name       PGUID NAMEDATALEN NAMEDATALEN  f b t \054 0  18 namein nameout namein nameout d _null_ ));
 #define NAMEOID 19
 
 DATA(insert OID = 20 (  char16     PGUID 16  16 f b t \054 0  18 char16in char16out char16in char16out i _null_ ));
-/*DATA(insert OID = 20 (  dt         PGUID  4  10 t b t \054 0   0 dtin dtout dtin dtout i _null_ )); */
 DATA(insert OID = 21 (  int2       PGUID  2   5 t b t \054 0   0 int2in int2out int2in int2out s _null_ ));
-
 #define INT2OID		21
 
 DATA(insert OID = 22 (  int28      PGUID 16  50 f b t \054 0  21 int28in int28out int28in int28out i _null_ ));
-
 /*
  * XXX -- the implementation of int28's in postgres is a hack, and will
  *	  go away someday.  until that happens, there is a case (in the
@@ -168,12 +164,9 @@ DATA(insert OID = 22 (  int28      PGUID 16  50 f b t \054 0  21 int28in int28ou
  *	  over piles of int28's on the sidewalk.  in order to do so, we
  *	  need the OID of the int28 row from pg_type.
  */
-
 #define INT28OID	22
 
-
 DATA(insert OID = 23 (  int4       PGUID  4  10 t b t \054 0   0 int4in int4out int4in int4out i _null_ ));
-
 #define INT4OID		23
 
 DATA(insert OID = 24 (  regproc    PGUID  4  16 t b t \054 0   0 regprocin regprocout regprocin regprocout i _null_ ));
@@ -181,7 +174,6 @@ DATA(insert OID = 25 (  text       PGUID -1  -1 f b t \054 0  18 textin textout
 #define TEXTOID 25
 
 DATA(insert OID = 26 (  oid        PGUID  4  10 t b t \054 0   0 int4in int4out int4in int4out i _null_ ));
-
 #define OIDOID		26
 
 DATA(insert OID = 27 (  tid        PGUID  6  19 f b t \054 0   0 tidin tidout tidin tidout i _null_ ));
@@ -232,21 +224,20 @@ DATA(insert OID = 605 (  filename  PGUID 256 -1 f b t \054 0 18 filename_in file
 
 /* OIDS 700 - 799 */
 
-#define FLOAT4OID 700
-
 DATA(insert OID = 700 (  float4    PGUID  4  12 f b t \054 0   0 float4in float4out float4in float4out i _null_ ));
-
-
-#define FLOAT8OID 701
-
+#define FLOAT4OID 700
 DATA(insert OID = 701 (  float8    PGUID  8  24 f b t \054 0   0 float8in float8out float8in float8out d _null_ ));
+#define FLOAT8OID 701
 DATA(insert OID = 702 (  abstime   PGUID  4  20 t b t \054 0   0 nabstimein nabstimeout nabstimein nabstimeout i _null_ ));
 DATA(insert OID = 703 (  reltime   PGUID  4  20 t b t \054 0   0 reltimein reltimeout reltimein reltimeout i _null_ ));
 DATA(insert OID = 704 (  tinterval PGUID 12  47 f b t \054 0   0 tintervalin tintervalout tintervalin tintervalout i _null_ ));
 DATA(insert OID = 705 (  unknown   PGUID -1  -1 f b t \054 0   18 textin textout textin textout i _null_ ));
-
 #define UNKNOWNOID	705
 
+DATA(insert OID = 790 (  money     PGUID  4  47 f b t \054 0    0 cash_in cash_out cash_in cash_out i _null_ ));
+#define CASHOID	790
+DATA(insert OID = 791 (  _money    PGUID  -1 -1 f b t \054 0  790 array_in array_out array_in array_out i _null_ ));
+
 /* OIDS 800 - 899 */
 DATA(insert OID = 810 (  oidint2   PGUID  6  20 f b t \054 0   0 oidint2in oidint2out oidint2in oidint2out i _null_ ));
 
@@ -292,10 +283,10 @@ DATA(insert OID = 1039 (  _char2     PGUID -1  -1 f b t \054 0  409 array_in arr
 DATA(insert OID = 1040 (  _char4     PGUID -1  -1 f b t \054 0  410 array_in array_out array_in array_out i _null_ ));
 DATA(insert OID = 1041 (  _char8     PGUID -1  -1 f b t \054 0  411 array_in array_out array_in array_out i _null_ ));
 
-#define	BPCHAROID	1042
 DATA(insert OID = 1042 ( bpchar      PGUID -1  -1 f b t \054 0  18 bpcharin bpcharout bpcharin bpcharout i _null_ ));
-#define	VARCHAROID	1043
+#define	BPCHAROID	1042
 DATA(insert OID = 1043 ( varchar     PGUID -1  -1 f b t \054 0  18 varcharin varcharout varcharin varcharout i _null_ ));
+#define	VARCHAROID	1043
 
 DATA(insert OID = 1082 ( date        PGUID  4  10 t b t \054 0  0 date_in date_out date_in date_out i _null_ ));
 #define DATEOID		1082
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7d780b7fc11..dc5b571fca9 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.14 1997/04/02 18:31:52 scrappy Exp $
+ * $Id: builtins.h,v 1.15 1997/04/15 17:41:35 scrappy Exp $
  *
  * NOTES
  *    This should normally only be included by fmgr.h.
@@ -22,6 +22,7 @@
 #include <utils/geo_decls.h>
 #include <utils/datetime.h>
 #include <utils/nabstime.h>
+#include <utils/cash.h>
 #include <utils/rel.h>
 
 /*
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 02655c88747..2cfb29313a3 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -1,16 +1,35 @@
 /*
-cash.h
-Written by D'Arcy J.M. Cain
+ * cash.h
+ * Written by D'Arcy J.M. Cain
+ *
+ * Functions to allow input and output of money normally but store
+ *  and handle it as long integers.
+ */
 
-functions to allow input and output of money normally but store
-and handle it as long integers
-*/
+#ifndef CASH_H
+#define CASH_H
 
-#ifndef		_CASH_H
-#define		_CASH_H
+typedef long int Cash;
 
-const char	*cash_out(long value);
-long		cash_in(const char *str);
-const char	*cash_words_out(long value);
+const char *cash_out(Cash *value);
+Cash *cash_in(const char *str);
 
-#endif		/* _CASH_H */
+bool cash_eq(Cash *c1, Cash *c2);
+bool cash_ne(Cash *c1, Cash *c2);
+bool cash_lt(Cash *c1, Cash *c2);
+bool cash_le(Cash *c1, Cash *c2);
+bool cash_gt(Cash *c1, Cash *c2);
+bool cash_ge(Cash *c1, Cash *c2);
+
+Cash *cash_pl(Cash *c1, Cash *c2);
+Cash *cash_mi(Cash *c1, Cash *c2);
+Cash *cash_mul(Cash *c, float8 *f);
+Cash *cash_div(Cash *c, float8 *f);
+
+Cash *cashlarger(Cash *c1, Cash *c2);
+Cash *cashsmaller(Cash *c1, Cash *c2);
+
+const char *cash_words_out(Cash *value);
+static const char *num_word(Cash value);
+
+#endif /* CASH_H */
-- 
GitLab