On Apr 1, 2010, at 7:57 AM, Kevin Grittner wrote:
> I'm inclined to think it's better to have an explicit cast from
> money to numeric, as long as it is exact, and leave the division of
> money by money as float8.  It does sort of beg the question of
> whether we should support a cast back in the other direction,
> though.  I think that would wrap this all up in a tidy package.

OK. Here is the whole thing in C:

#include <postgres.h>
#include <fmgr.h>
#include <utils/cash.h>
#include <utils/numeric.h>
#include <utils/pg_locale.h>

PG_MODULE_MAGIC;

extern Datum int8_numeric(PG_FUNCTION_ARGS);
extern Datum numeric_div(PG_FUNCTION_ARGS);
extern Datum numeric_mul(PG_FUNCTION_ARGS);
extern Datum numeric_int8(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(cash_div_cash);

/* 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);
}

PG_FUNCTION_INFO_V1(cash_numeric);

/* 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);
}

PG_FUNCTION_INFO_V1(numeric_cash);

/* 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);
}

--------------------------------------------------------------------------------------

And the SQL to load it:

CREATE FUNCTION cash_div_cash(money, money) RETURNS double precision
    LANGUAGE c IMMUTABLE STRICT
    AS '$libdir/divide_money', 'cash_div_cash';

CREATE FUNCTION cash_numeric(money) RETURNS numeric
    LANGUAGE c IMMUTABLE STRICT
    AS '$libdir/divide_money', 'cash_numeric';

CREATE FUNCTION numeric_cash(numeric) RETURNS money
    LANGUAGE c IMMUTABLE STRICT
    AS '$libdir/divide_money', 'numeric_cash';

CREATE CAST (money AS numeric) WITH FUNCTION public.cash_numeric(money) AS 
ASSIGNMENT;

CREATE CAST (numeric AS money) WITH FUNCTION public.numeric_cash(numeric) AS 
ASSIGNMENT;

CREATE OPERATOR / (
    PROCEDURE = cash_div_cash,
    LEFTARG = money,
    RIGHTARG = money
);


-- 
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

Reply via email to