diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index 76962bdcf6d85322aef6737fffbcc34aac3a34db..d04f3d38c22922c7ff0cef6d83d01c32c06a4afe 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -1,15 +1,19 @@
 /*
  * 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.
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/cash.c,v 1.68 2006/07/14 14:52:23 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/cash.c,v 1.69 2007/01/03 01:19:50 darcy Exp $
  */
 
 #include "postgres.h"
@@ -23,17 +27,12 @@
 #include "utils/cash.h"
 #include "utils/pg_locale.h"
 
-
-static const char *num_word(Cash value);
-
-/* when we go to 64 bit values we will have to modify this */
-#define CASH_BUFSZ		24
+#define CASH_BUFSZ		36
 
 #define TERMINATOR		(CASH_BUFSZ - 1)
 #define LAST_PAREN		(TERMINATOR - 1)
 #define LAST_DIGIT		(LAST_PAREN - 1)
 
-
 /*
  * Cash is a pass-by-ref SQL type, so we must pass and return pointers.
  * These macros and support routine hide the pass-by-refness.
@@ -41,6 +40,65 @@ static const char *num_word(Cash value);
 #define PG_GETARG_CASH(n)  (* ((Cash *) PG_GETARG_POINTER(n)))
 #define PG_RETURN_CASH(x)  return CashGetDatum(x)
 
+
+
+/*************************************************************************
+ * 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", "forty", "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() */
+
 static Datum
 CashGetDatum(Cash value)
 {
@@ -56,12 +114,6 @@ CashGetDatum(Cash value)
  * 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 multibyte.
- *	- thomas 1998-03-01
  */
 Datum
 cash_in(PG_FUNCTION_ARGS)
@@ -74,11 +126,11 @@ cash_in(PG_FUNCTION_ARGS)
 	int			seen_dot = 0;
 	const char *s = str;
 	int			fpoint;
-	char	   *csymbol;
 	char		dsymbol,
 				ssymbol,
-				psymbol,
-			   *nsymbol;
+				psymbol;
+	const char *nsymbol,
+			   *csymbol;
 
 	struct lconv *lconvert = PGLC_localeconv();
 
@@ -120,6 +172,7 @@ cash_in(PG_FUNCTION_ARGS)
 
 	/* a leading minus or paren signifies a negative number */
 	/* again, better heuristics needed */
+	/* XXX - doesn't properly check for balanced parens - djmc */
 	if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
 	{
 		sgn = -1;
@@ -152,7 +205,7 @@ cash_in(PG_FUNCTION_ARGS)
 
 	for (;; s++)
 	{
-		/* we look for digits as int4 as we have less */
+		/* we look for digits as int8 as we have less */
 		/* than the required number of decimal places */
 		if (isdigit((unsigned char) *s) && dec < fpoint)
 		{
@@ -161,14 +214,14 @@ cash_in(PG_FUNCTION_ARGS)
 			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... */
 		}
+		/* "thousands" separator? then skip... */
 		else if (*s == ssymbol)
 		{
 
@@ -187,7 +240,9 @@ cash_in(PG_FUNCTION_ARGS)
 		}
 	}
 
-	while (isspace((unsigned char) *s) || *s == '0' || *s == ')')
+	/* should only be trailing digits followed by whitespace or closing paren */
+	while (isdigit(*s)) s++;
+	while (isspace((unsigned char) *s) || *s == ')')
 		s++;
 
 	if (*s != '\0')
@@ -223,9 +278,9 @@ cash_out(PG_FUNCTION_ARGS)
 	int			points,
 				mon_group;
 	char		comma;
-	char	   *csymbol,
-				dsymbol,
+	const char *csymbol,
 			   *nsymbol;
+	char		dsymbol;
 	char		convention;
 
 	struct lconv *lconvert = PGLC_localeconv();
@@ -276,8 +331,8 @@ cash_out(PG_FUNCTION_ARGS)
 		else if (comma && count % (mon_group + 1) == comma_position)
 			buf[count--] = comma;
 
-		buf[count--] = ((unsigned int) value % 10) + '0';
-		value = ((unsigned int) value) / 10;
+		buf[count--] = ((uint64) value % 10) + '0';
+		value = ((uint64) value) / 10;
 	}
 
 	strncpy((buf + count - strlen(csymbol) + 1), csymbol, strlen(csymbol));
@@ -470,9 +525,6 @@ flt8_mul_cash(PG_FUNCTION_ARGS)
 
 /* 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
  */
 Datum
 cash_div_flt8(PG_FUNCTION_ARGS)
@@ -490,6 +542,7 @@ cash_div_flt8(PG_FUNCTION_ARGS)
 	PG_RETURN_CASH(result);
 }
 
+
 /* cash_mul_flt4()
  * Multiply cash by float4.
  */
@@ -523,8 +576,6 @@ flt4_mul_cash(PG_FUNCTION_ARGS)
 /* 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
  */
 Datum
 cash_div_flt4(PG_FUNCTION_ARGS)
@@ -543,6 +594,56 @@ cash_div_flt4(PG_FUNCTION_ARGS)
 }
 
 
+/* 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;
+
+	result = c * i;
+	PG_RETURN_CASH(result);
+}
+
+
+/* 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.
+ */
+Datum
+cash_div_int8(PG_FUNCTION_ARGS)
+{
+	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);
+
+	PG_RETURN_CASH(result);
+}
+
+
 /* cash_mul_int4()
  * Multiply cash by int4.
  */
@@ -550,7 +651,7 @@ Datum
 cash_mul_int4(PG_FUNCTION_ARGS)
 {
 	Cash		c = PG_GETARG_CASH(0);
-	int32		i = PG_GETARG_INT32(1);
+	int64		i = PG_GETARG_INT64(1);
 	Cash		result;
 
 	result = c * i;
@@ -576,14 +677,12 @@ int4_mul_cash(PG_FUNCTION_ARGS)
 /* 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
  */
 Datum
 cash_div_int4(PG_FUNCTION_ARGS)
 {
 	Cash		c = PG_GETARG_CASH(0);
-	int32		i = PG_GETARG_INT32(1);
+	int64		i = PG_GETARG_INT64(1);
 	Cash		result;
 
 	if (i == 0)
@@ -628,8 +727,6 @@ int2_mul_cash(PG_FUNCTION_ARGS)
 /* 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
  */
 Datum
 cash_div_int2(PG_FUNCTION_ARGS)
@@ -677,7 +774,6 @@ cashsmaller(PG_FUNCTION_ARGS)
 	PG_RETURN_CASH(result);
 }
 
-
 /* cash_words()
  * This converts a int4 as well but to a representation using words
  * Obviously way North American centric - sorry
@@ -686,13 +782,16 @@ Datum
 cash_words(PG_FUNCTION_ARGS)
 {
 	Cash		value = PG_GETARG_CASH(0);
-	unsigned int val;
+	uint64 val;
 	char		buf[256];
 	char	   *p = buf;
 	Cash		m0;
 	Cash		m1;
 	Cash		m2;
 	Cash		m3;
+	Cash		m4;
+	Cash		m5;
+	Cash		m6;
 	text	   *result;
 
 	/* work with positive numbers */
@@ -706,12 +805,33 @@ cash_words(PG_FUNCTION_ARGS)
 		buf[0] = '\0';
 
 	/* Now treat as unsigned, to avoid trouble at INT_MIN */
-	val = (unsigned int) value;
+	val = (uint64) value;
 
-	m0 = val % 100;				/* cents */
-	m1 = (val / 100) % 1000;	/* hundreds */
-	m2 = (val / 100000) % 1000; /* thousands */
-	m3 = val / 100000000 % 1000;	/* millions */
+	m0 = val % 100ll;				/* cents */
+	m1 = (val / 100ll) % 1000;	/* hundreds */
+	m2 = (val / 100000ll) % 1000; /* thousands */
+	m3 = val / 100000000ll % 1000;	/* millions */
+	m4 = val / 100000000000ll % 1000;	/* billions */
+	m5 = val / 100000000000000ll % 1000;	/* trillions */
+	m6 = val / 100000000000000000ll % 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)
 	{
@@ -745,61 +865,3 @@ cash_words(PG_FUNCTION_ARGS)
 
 	PG_RETURN_TEXT_P(result);
 }
-
-
-/*************************************************************************
- * 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", "forty", "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_type.h b/src/include/catalog/pg_type.h
index 65f8b9deea8399661bfb0cf38eb4c72adac525c8..a961bf01ed3fa026dda859141aea511504f3509f 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.176 2006/12/30 21:21:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.177 2007/01/03 01:19:51 darcy Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -388,10 +388,10 @@ DATA(insert OID = 718 (  circle    PGNSP PGUID	24 f b t \054 0 0 circle_in circl
 DESCR("geometric circle '(center,radius)'");
 #define CIRCLEOID		718
 DATA(insert OID = 719 (  _circle   PGNSP PGUID	-1 f b t \054 0  718 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 790 (  money	   PGNSP PGUID	 4 f b t \054 0 0 cash_in cash_out cash_recv cash_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 790 (  money	   PGNSP PGUID	 8 f b t \054 0 0 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("monetary amounts, $d,ddd.cc");
 #define CASHOID 790
-DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 800 - 899 */
 DATA(insert OID = 829 ( macaddr    PGNSP PGUID	6 f b t \054 0 0 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 193fe9aab574f98fb3d1601a86a2131266081adb..c58ae72563b9506878ffd49ebe1ba3601b931fbe 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -3,7 +3,7 @@
  * Written by D'Arcy J.M. Cain
  *
  * Functions to allow input and output of money normally but store
- *	and handle it as int4.
+ *	and handle it as 64 bit integer.
  */
 
 #ifndef CASH_H
@@ -11,8 +11,7 @@
 
 #include "fmgr.h"
 
-/* if we store this as 4 bytes, we better make it int, not long, bjm */
-typedef int32 Cash;
+typedef int64 Cash;
 
 extern Datum cash_in(PG_FUNCTION_ARGS);
 extern Datum cash_out(PG_FUNCTION_ARGS);
@@ -31,16 +30,20 @@ extern Datum cash_pl(PG_FUNCTION_ARGS);
 extern Datum cash_mi(PG_FUNCTION_ARGS);
 
 extern Datum cash_mul_flt8(PG_FUNCTION_ARGS);
-extern Datum cash_div_flt8(PG_FUNCTION_ARGS);
 extern Datum flt8_mul_cash(PG_FUNCTION_ARGS);
+extern Datum cash_div_flt8(PG_FUNCTION_ARGS);
 
 extern Datum cash_mul_flt4(PG_FUNCTION_ARGS);
-extern Datum cash_div_flt4(PG_FUNCTION_ARGS);
 extern Datum flt4_mul_cash(PG_FUNCTION_ARGS);
+extern Datum cash_div_flt4(PG_FUNCTION_ARGS);
+
+extern Datum cash_mul_int8(PG_FUNCTION_ARGS);
+extern Datum int8_mul_cash(PG_FUNCTION_ARGS);
+extern Datum cash_div_int8(PG_FUNCTION_ARGS);
 
 extern Datum cash_mul_int4(PG_FUNCTION_ARGS);
-extern Datum cash_div_int4(PG_FUNCTION_ARGS);
 extern Datum int4_mul_cash(PG_FUNCTION_ARGS);
+extern Datum cash_div_int4(PG_FUNCTION_ARGS);
 
 extern Datum cash_mul_int2(PG_FUNCTION_ARGS);
 extern Datum int2_mul_cash(PG_FUNCTION_ARGS);