From f5e524d92be609c709825be8995bf77f10880c3b Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Tue, 5 Apr 2011 09:35:43 -0400 Subject: [PATCH] Add casts from int4 and int8 to numeric. Joey Adams, per gripe from Ramanujam. Review by myself and Tom Lane. --- doc/src/sgml/datatype.sgml | 17 +++++--- src/backend/utils/adt/cash.c | 62 +++++++++++++++++++++++++++- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_cast.h | 2 + src/include/catalog/pg_proc.h | 4 ++ src/include/utils/cash.h | 3 ++ src/test/regress/expected/money.out | 63 +++++++++++++++++++++++++++++ src/test/regress/sql/money.sql | 15 +++++++ 8 files changed, 161 insertions(+), 7 deletions(-) diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index ecc79e27fc..13b888dff8 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -886,15 +886,22 @@ ALTER SEQUENCE tablename_ - 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: + Values of the numeric, int, and + bigint data types can be cast to money. + Conversion from the real and double precision + data types can be done by casting to numeric first, for + example: -SELECT 1234::numeric::money; +SELECT '12.34'::float8::numeric::money; + However, this is not recommended. Floating point numbers should not be + used to handle money due to the potential for rounding errors. + + + 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, for example: + precision, and must also be done in two stages: SELECT '52093.89'::money::numeric::float8; diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index 67f51280c3..4c3279759d 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -26,6 +26,7 @@ #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" @@ -92,7 +93,6 @@ num_word(Cash value) return buf; } /* num_word() */ - /* cash_in() * Convert a string to a cash data type. * Format is [$]###[,]###[.##] @@ -938,3 +938,63 @@ numeric_cash(PG_FUNCTION_ARGS) 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); +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e965909219..b74526b3b7 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201103201 +#define CATALOG_VERSION_NO 201104051 #endif diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index bf8a6fcb77..f7b7b76833 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -126,6 +126,8 @@ 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 )); +DATA(insert ( 23 790 3811 a f )); +DATA(insert ( 20 790 3812 a f )); /* Allow explicit coercions between int4 and bool */ DATA(insert ( 23 16 2557 e f )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index cff64ba6b0..7919a40487 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -971,6 +971,10 @@ DATA(insert OID = 3823 ( numeric PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1700 DESCR("convert money to numeric"); DATA(insert OID = 3824 ( money PGNSP PGUID 12 1 0 0 f f f t f s 1 0 790 "1700" _null_ _null_ _null_ _null_ numeric_cash _null_ _null_ _null_ )); DESCR("convert numeric to money"); +DATA(insert OID = 3811 ( money PGNSP PGUID 12 1 0 0 f f f t f s 1 0 790 "23" _null_ _null_ _null_ _null_ int4_cash _null_ _null_ _null_ )); +DESCR("convert int4 to money"); +DATA(insert OID = 3812 ( money PGNSP PGUID 12 1 0 0 f f f t f s 1 0 790 "20" _null_ _null_ _null_ _null_ int8_cash _null_ _null_ _null_ )); +DESCR("convert int8 to money"); /* OIDS 900 - 999 */ diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h index 81b51ad68f..3a491f9231 100644 --- a/src/include/utils/cash.h +++ b/src/include/utils/cash.h @@ -67,4 +67,7 @@ extern Datum cash_words(PG_FUNCTION_ARGS); extern Datum cash_numeric(PG_FUNCTION_ARGS); extern Datum numeric_cash(PG_FUNCTION_ARGS); +extern Datum int4_cash(PG_FUNCTION_ARGS); +extern Datum int8_cash(PG_FUNCTION_ARGS); + #endif /* CASH_H */ diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out index ec4169cbb0..538235c4cc 100644 --- a/src/test/regress/expected/money.out +++ b/src/test/regress/expected/money.out @@ -185,3 +185,66 @@ SELECT * FROM money_data; $123.46 (1 row) +-- Cast int4/int8 to money +SELECT 1234567890::money; + money +------------------- + $1,234,567,890.00 +(1 row) + +SELECT 12345678901234567::money; + money +---------------------------- + $12,345,678,901,234,567.00 +(1 row) + +SELECT 123456789012345678::money; +ERROR: bigint out of range +SELECT 9223372036854775807::money; +ERROR: bigint out of range +SELECT (-12345)::money; + money +------------- + -$12,345.00 +(1 row) + +SELECT (-1234567890)::money; + money +-------------------- + -$1,234,567,890.00 +(1 row) + +SELECT (-12345678901234567)::money; + money +----------------------------- + -$12,345,678,901,234,567.00 +(1 row) + +SELECT (-123456789012345678)::money; +ERROR: bigint out of range +SELECT (-9223372036854775808)::money; +ERROR: bigint out of range +SELECT 1234567890::int4::money; + money +------------------- + $1,234,567,890.00 +(1 row) + +SELECT 12345678901234567::int8::money; + money +---------------------------- + $12,345,678,901,234,567.00 +(1 row) + +SELECT (-1234567890)::int4::money; + money +-------------------- + -$1,234,567,890.00 +(1 row) + +SELECT (-12345678901234567)::int8::money; + money +----------------------------- + -$12,345,678,901,234,567.00 +(1 row) + diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql index 580425e504..09b9476b70 100644 --- a/src/test/regress/sql/money.sql +++ b/src/test/regress/sql/money.sql @@ -56,3 +56,18 @@ SELECT * FROM money_data; DELETE FROM money_data; INSERT INTO money_data VALUES ('$123.459'); SELECT * FROM money_data; + +-- Cast int4/int8 to money +SELECT 1234567890::money; +SELECT 12345678901234567::money; +SELECT 123456789012345678::money; +SELECT 9223372036854775807::money; +SELECT (-12345)::money; +SELECT (-1234567890)::money; +SELECT (-12345678901234567)::money; +SELECT (-123456789012345678)::money; +SELECT (-9223372036854775808)::money; +SELECT 1234567890::int4::money; +SELECT 12345678901234567::int8::money; +SELECT (-1234567890)::int4::money; +SELECT (-12345678901234567)::int8::money; -- 2.40.0