*** ./doc/src/sgml/datatype.sgml.orig 2010-05-31 14:51:02.000000000 -0700 --- ./doc/src/sgml/datatype.sgml 2010-05-31 14:54:02.000000000 -0700 *************** *** 843,863 **** floating-point literals, as well as typical currency formatting, such as '$1,000.00'. Output is generally in the latter form but depends on the locale. ! Non-quoted numeric values can be converted to money by ! casting the numeric value to text and then ! money, for example: ! ! SELECT 1234::text::money; ! ! There is no simple way of doing the reverse in a locale-independent ! manner, namely casting a money value to a numeric type. ! If you know the currency symbol and thousands separator you can use ! regexp_replace(): ! SELECT regexp_replace('52093.89'::money::text, '[$,]', '', 'g')::numeric; Since the output of this data type is locale-sensitive, it might not --- 843,871 ---- floating-point literals, as well as typical currency formatting, such as '$1,000.00'. Output is generally in the latter form but depends on the locale. ! ! ! ! Values of the numeric data type can be cast to money. ! Other numeric types can be converted to money by casting to ! numeric first, for example: ! ! SELECT 1234::numeric::money; ! ! A money value can be cast to numeric without ! loss of precision. Conversion to other types could potentially lose precision, ! and it must be done in two stages: ! SELECT '52093.89'::money::numeric::float; + + + When a money value is divided by another money value, + the result is double precision (i.e. a pure number, not money); + the currency units cancel each other out in the division. + Since the output of this data type is locale-sensitive, it might not *** ./src/backend/utils/adt/cash.c.orig 2010-05-31 14:51:28.000000000 -0700 --- ./src/backend/utils/adt/cash.c 2010-05-31 14:54:02.000000000 -0700 *************** *** 27,32 **** --- 27,33 ---- #include "utils/builtins.h" #include "utils/cash.h" #include "utils/pg_locale.h" + #include "utils/numeric.h" #define CASH_BUFSZ 36 *************** *** 845,847 **** --- 846,944 ---- /* return as text datum */ PG_RETURN_TEXT_P(cstring_to_text(buf)); } + + /* + * The functions cash_div_cash(), cash_numeric(), and numeric_cash() + * were written by Andy Balholm . + */ + + /* 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; + + if (divisor == 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + + quotient = (float8)dividend / (float8)divisor; + PG_RETURN_FLOAT8(quotient); + } + + /* cash_numeric() + * Convert cash to numeric. + */ + Datum + cash_numeric(PG_FUNCTION_ARGS) + { + Cash money = PG_GETARG_CASH(0); + int fpoint; + int64 scale; + int i; + Numeric result; + Datum amount; + Datum numeric_scale; + Datum one; + + struct lconv *lconvert = PGLC_localeconv(); + + /* + * Find the number of digits after the decimal point. + * (These lines were copied from cash_in().) + */ + fpoint = lconvert->frac_digits; + if (fpoint < 0 || fpoint > 10) + fpoint = 2; + scale = 1; + for (i = 0; i < fpoint; i++) + scale *= 10; + + amount = DirectFunctionCall1(&int8_numeric, Int64GetDatum(money)); + one = DirectFunctionCall1(&int8_numeric, Int64GetDatum(1)); + numeric_scale = DirectFunctionCall1(&int8_numeric, Int64GetDatum(scale)); + numeric_scale = DirectFunctionCall2(&numeric_div, one, numeric_scale); + result = DatumGetNumeric(DirectFunctionCall2(&numeric_mul, amount, numeric_scale)); + + result->n_sign_dscale = NUMERIC_SIGN(result) | fpoint; /* Display the right number of decimal digits. */ + + 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(); + + /* + * Find the number of digits after the decimal point. + */ + fpoint = lconvert->frac_digits; + if (fpoint < 0 || fpoint > 10) + fpoint = 2; + scale = 1; + for (i = 0; i < fpoint; i++) + scale *= 10; + + numeric_scale = DirectFunctionCall1(&int8_numeric, Int64GetDatum(scale)); + amount = DirectFunctionCall2(&numeric_mul, amount, numeric_scale); + amount = DirectFunctionCall1(&numeric_int8, amount); + + result = DatumGetInt64(amount); + PG_RETURN_CASH(result); + } *** ./src/include/catalog/pg_cast.h.orig 2010-05-31 14:52:30.000000000 -0700 --- ./src/include/catalog/pg_cast.h 2010-05-31 14:54:02.000000000 -0700 *************** *** 124,129 **** --- 124,131 ---- DATA(insert ( 1700 23 1744 a f )); DATA(insert ( 1700 700 1745 i f )); DATA(insert ( 1700 701 1746 i f )); + DATA(insert ( 790 1700 3823 a f )); + DATA(insert ( 1700 790 3824 a f )); /* Allow explicit coercions between int4 and bool */ DATA(insert ( 23 16 2557 e f )); *** ./src/include/catalog/pg_operator.h.orig 2010-05-31 14:52:30.000000000 -0700 --- ./src/include/catalog/pg_operator.h 2010-05-31 14:54:02.000000000 -0700 *************** *** 943,948 **** --- 943,951 ---- DATA(insert OID = 2992 ( "<=" PGNSP PGUID b f f 2249 2249 16 2993 2991 record_le scalarltsel scalarltjoinsel )); DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel )); + /* enhancement to money type */ + DATA(insert OID = 3825 ( "/" PGNSP PGUID b f f 790 790 701 0 0 cash_div_cash - - )); + /* * function prototypes *** ./src/include/catalog/pg_proc.h.orig 2010-05-31 14:52:30.000000000 -0700 --- ./src/include/catalog/pg_proc.h 2010-05-31 14:54:02.000000000 -0700 *************** *** 4778,4783 **** --- 4778,4791 ---- DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ )); DESCR("fetch the Nth row value"); + /* enhancements to money type */ + DATA(insert OID = 3822 ( cash_div_cash PGNSP PGUID 12 1 0 0 f f f t f i 2 0 701 "790 790" _null_ _null_ _null_ _null_ cash_div_cash _null_ _null_ _null_ )); + DESCR("divide"); + DATA(insert OID = 3823 ( numeric PGNSP PGUID 12 1 0 0 f f f t f i 1 0 1700 "790" _null_ _null_ _null_ _null_ cash_numeric _null_ _null_ _null_ )); + DESCR("(internal)"); + DATA(insert OID = 3824 ( money PGNSP PGUID 12 1 0 0 f f f t f i 1 0 790 "1700" _null_ _null_ _null_ _null_ numeric_cash _null_ _null_ _null_ )); + DESCR("(internal)"); + /* * Symbolic values for provolatile column: these indicate whether the result *** ./src/include/utils/cash.h.orig 2010-05-31 14:52:44.000000000 -0700 --- ./src/include/utils/cash.h 2010-05-31 14:54:02.000000000 -0700 *************** *** 63,66 **** --- 63,70 ---- extern Datum cash_words(PG_FUNCTION_ARGS); + extern Datum cash_div_cash(PG_FUNCTION_ARGS); + extern Datum cash_numeric(PG_FUNCTION_ARGS); + extern Datum numeric_cash(PG_FUNCTION_ARGS); + #endif /* CASH_H */