dividing-money-2.diff
application/octet-stream
Filename: dividing-money-2.diff
Type: application/octet-stream
Part: 0
Message:
Re: dividing money by money
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| doc/src/sgml/datatype.sgml | 17 | 0 |
| src/backend/utils/adt/cash.c | 94 | 0 |
| src/include/catalog/pg_cast.h | 2 | 0 |
| src/include/catalog/pg_operator.h | 1 | 0 |
| src/include/catalog/pg_proc.h | 6 | 0 |
| src/include/utils/cash.h | 4 | 0 |
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 843,865 **** ALTER SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceab
floating-point literals, as well as typical
currency formatting, such as <literal>'$1,000.00'</literal>.
Output is generally in the latter form but depends on the locale.
! Non-quoted numeric values can be converted to <type>money</type> by
! casting the numeric value to <type>text</type> and then
! <type>money</type>, for example:
<programlisting>
! SELECT 1234::text::money;
</programlisting>
! There is no simple way of doing the reverse in a locale-independent
! manner, namely casting a <type>money</type> value to a numeric type.
! If you know the currency symbol and thousands separator you can use
! <function>regexp_replace()</>:
<programlisting>
! SELECT regexp_replace('52093.89'::money::text, '[$,]', '', 'g')::numeric;
</programlisting>
</para>
<para>
Since the output of this data type is locale-sensitive, it might not
work to load <type>money</> data into a database that has a different
setting of <varname>lc_monetary</>. To avoid problems, before
--- 843,873 ----
floating-point literals, as well as typical
currency formatting, such as <literal>'$1,000.00'</literal>.
Output is generally in the latter form but depends on the locale.
! </para>
!
! <para>
! Values of the <type>numeric</type> data type can be cast to <type>money</type>.
! Other numeric types can be converted to <type>money</type> by casting to
! <type>numeric</type> first, for example:
<programlisting>
! SELECT 1234::numeric::money;
</programlisting>
! A <type>money</type> value can be cast to <type>numeric</type> without
! loss of precision. Conversion to other types could potentially lose precision,
! and it must be done in two stages:
<programlisting>
! SELECT '52093.89'::money::numeric::float;
</programlisting>
</para>
<para>
+ When a <type>money</type> value is divided by another <type>money</type> value,
+ the result is <type>double precision</type> (i.e. a pure number, not money);
+ the currency units cancel each other out in the division.
+ </para>
+
+ <para>
Since the output of this data type is locale-sensitive, it might not
work to load <type>money</> data into a database that has a different
setting of <varname>lc_monetary</>. To avoid problems, before
*** a/src/backend/utils/adt/cash.c
--- b/src/backend/utils/adt/cash.c
***************
*** 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 **** cash_words(PG_FUNCTION_ARGS)
--- 846,941 ----
/* return as text datum */
PG_RETURN_TEXT_P(cstring_to_text(buf));
}
+
+ /* 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);
+ }
*** a/src/include/catalog/pg_cast.h
--- b/src/include/catalog/pg_cast.h
***************
*** 124,129 **** DATA(insert ( 1700 21 1783 a f ));
--- 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 ));
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 415,420 **** DATA(insert OID = 915 ( "/" PGNSP PGUID b f f 790 21 790 0 0 cash_div_
--- 415,421 ----
DATA(insert OID = 916 ( "*" PGNSP PGUID b f f 701 790 790 908 0 flt8_mul_cash - - ));
DATA(insert OID = 917 ( "*" PGNSP PGUID b f f 23 790 790 912 0 int4_mul_cash - - ));
DATA(insert OID = 918 ( "*" PGNSP PGUID b f f 21 790 790 914 0 int2_mul_cash - - ));
+ DATA(insert OID = 3825 ( "/" PGNSP PGUID b f f 790 790 701 0 0 cash_div_cash - - ));
DATA(insert OID = 965 ( "^" PGNSP PGUID b f f 701 701 701 0 0 dpow - - ));
DATA(insert OID = 966 ( "+" PGNSP PGUID b f f 1034 1033 1034 0 0 aclinsert - - ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 1197,1202 **** DATA(insert OID = 919 ( flt8_mul_cash PGNSP PGUID 12 1 0 0 f f f t f i 2 0
--- 1197,1208 ----
DESCR("multiply");
DATA(insert OID = 935 ( cash_words PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "790" _null_ _null_ _null_ _null_ cash_words _null_ _null_ _null_ ));
DESCR("output amount as words");
+ 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)");
/* OIDS 900 - 999 */
*** a/src/include/utils/cash.h
--- b/src/include/utils/cash.h
***************
*** 63,66 **** extern Datum cashsmaller(PG_FUNCTION_ARGS);
--- 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 */