blob: 481f2afa486723192271d8d4a51a31e8862458ed [file] [log] [blame]
#include "postgres.h"
#include <math.h>
#include "funcapi.h"
#include "fmgr.h"
#include "utils/numeric.h"
#include "utils/builtins.h"
#include "orafce.h"
#include "builtins.h"
PG_FUNCTION_INFO_V1(orafce_reminder_smallint);
PG_FUNCTION_INFO_V1(orafce_reminder_int);
PG_FUNCTION_INFO_V1(orafce_reminder_bigint);
PG_FUNCTION_INFO_V1(orafce_reminder_numeric);
/*
* CREATE OR REPLACE FUNCTION oracle.remainder(smallint, smallint)
* RETURNS smallint
*/
Datum
orafce_reminder_smallint(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
if (arg2 == 0)
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
}
if (arg2 == -1)
PG_RETURN_INT16(0);
PG_RETURN_INT16(arg1 - ((int16) round(((double) arg1) / ((double) arg2)) * arg2));
}
/*
* CREATE OR REPLACE FUNCTION oracle.remainder(int, int)
* RETURNS int
*/
Datum
orafce_reminder_int(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
if (arg2 == 0)
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
}
if (arg2 == -1)
PG_RETURN_INT32(0);
PG_RETURN_INT32(arg1 - ((int32) round(((double) arg1) / ((double) arg2)) * arg2));
}
/*
* CREATE OR REPLACE FUNCTION oracle.remainder(bigint, bigint)
* RETURNS bigint
*/
Datum
orafce_reminder_bigint(PG_FUNCTION_ARGS)
{
int64 arg1 = PG_GETARG_INT64(0);
int64 arg2 = PG_GETARG_INT64(1);
if (arg2 == 0)
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
}
if (arg2 == -1)
PG_RETURN_INT32(0);
PG_RETURN_INT64(arg1 - ((int64) round(((long double) arg1) / ((long double) arg2)) * arg2));
}
/*
* This will handle NaN and Infinity cases
*/
static Numeric
duplicate_numeric(Numeric num)
{
Numeric res;
res = (Numeric) palloc(VARSIZE(num));
memcpy(res, num, VARSIZE(num));
return res;
}
static Numeric
get_numeric_in(const char *str)
{
return DatumGetNumeric(
DirectFunctionCall3(numeric_in,
CStringGetDatum(str),
ObjectIdGetDatum(0),
Int32GetDatum(-1)));
}
static bool
orafce_numeric_is_inf(Numeric num)
{
#if PG_VERSION_NUM >= 140000
return numeric_is_inf(num);
#else
/* older releases doesn't support +-Infinitity in numeric type */
return false;
#endif
}
/*
* CREATE OR REPLACE FUNCTION oracle.remainder(numeric, numeric)
* RETURNS numeric
*/
Datum
orafce_reminder_numeric(PG_FUNCTION_ARGS)
{
Numeric num1 = PG_GETARG_NUMERIC(0);
Numeric num2 = PG_GETARG_NUMERIC(1);
Numeric result;
float8 val2;
if (numeric_is_nan(num1))
duplicate_numeric(num1);
if (numeric_is_nan(num2))
duplicate_numeric(num2);
val2 = DatumGetFloat8(DirectFunctionCall1(numeric_float8, NumericGetDatum(num2)));
if (val2 == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
if (orafce_numeric_is_inf(num1))
PG_RETURN_NUMERIC(get_numeric_in("NaN"));
if (orafce_numeric_is_inf(num2))
duplicate_numeric(num1);
#if PG_VERSION_NUM >= 150000
result = numeric_sub_opt_error(
num1,
numeric_mul_opt_error(
DatumGetNumeric(
DirectFunctionCall2(
numeric_round,
NumericGetDatum(
numeric_div_opt_error(num1, num2,NULL)),
Int32GetDatum(0))),
num2,
NULL),
NULL);
#else
result = DatumGetNumeric(
DirectFunctionCall2(numeric_sub,
NumericGetDatum(num1),
DirectFunctionCall2(numeric_mul,
DirectFunctionCall2(numeric_round,
DirectFunctionCall2(numeric_div,
NumericGetDatum(num1),
NumericGetDatum(num2)),
Int32GetDatum(0)),
NumericGetDatum(num2))));
#endif
PG_RETURN_NUMERIC(result);
}