diff options
author | Fujii Masao <fujii@postgresql.org> | 2020-06-30 23:55:07 +0900 |
---|---|---|
committer | Fujii Masao <fujii@postgresql.org> | 2020-06-30 23:55:07 +0900 |
commit | 9bae7e4cde7c9786ee61dac4a3e032b346350a88 (patch) | |
tree | 11fc6cfc0c7589b2857a68080ebcf0cd54f064b1 /src/backend/utils/adt/numeric.c | |
parent | 324435eb14e4f41cd430f96c9b13ad9b160e45e4 (diff) | |
download | postgresql-9bae7e4cde7c9786ee61dac4a3e032b346350a88.tar.gz postgresql-9bae7e4cde7c9786ee61dac4a3e032b346350a88.zip |
Add +(pg_lsn,numeric) and -(pg_lsn,numeric) operators.
By using these operators, the number of bytes can be added into and
subtracted from LSN.
Bump catalog version.
Author: Fujii Masao
Reviewed-by: Kyotaro Horiguchi, Michael Paquier, Asif Rehman
Discussion: https://postgr.es/m/ed9f7f74-e996-67f8-554a-52ebd3779b3b@oss.nttdata.com
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r-- | src/backend/utils/adt/numeric.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 5f23f2afac8..1773fa292e4 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -41,6 +41,7 @@ #include "utils/guc.h" #include "utils/int8.h" #include "utils/numeric.h" +#include "utils/pg_lsn.h" #include "utils/sortsupport.h" /* ---------- @@ -472,6 +473,7 @@ static void apply_typmod(NumericVar *var, int32 typmod); static bool numericvar_to_int32(const NumericVar *var, int32 *result); static bool numericvar_to_int64(const NumericVar *var, int64 *result); static void int64_to_numericvar(int64 val, NumericVar *var); +static bool numericvar_to_uint64(const NumericVar *var, uint64 *result); #ifdef HAVE_INT128 static bool numericvar_to_int128(const NumericVar *var, int128 *result); static void int128_to_numericvar(int128 val, NumericVar *var); @@ -3692,6 +3694,30 @@ numeric_float4(PG_FUNCTION_ARGS) } +Datum +numeric_pg_lsn(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + NumericVar x; + XLogRecPtr result; + + if (NUMERIC_IS_NAN(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to pg_lsn"))); + + /* Convert to variable format and thence to pg_lsn */ + init_var_from_num(num, &x); + + if (!numericvar_to_uint64(&x, (uint64 *) &result)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_lsn out of range"))); + + PG_RETURN_LSN(result); +} + + /* ---------------------------------------------------------------------- * * Aggregate functions @@ -6742,6 +6768,78 @@ int64_to_numericvar(int64 val, NumericVar *var) var->weight = ndigits - 1; } +/* + * Convert numeric to uint64, rounding if needed. + * + * If overflow, return false (no error is raised). Return true if okay. + */ +static bool +numericvar_to_uint64(const NumericVar *var, uint64 *result) +{ + NumericDigit *digits; + int ndigits; + int weight; + int i; + uint64 val; + NumericVar rounded; + + /* Round to nearest integer */ + init_var(&rounded); + set_var_from_var(var, &rounded); + round_var(&rounded, 0); + + /* Check for zero input */ + strip_var(&rounded); + ndigits = rounded.ndigits; + if (ndigits == 0) + { + *result = 0; + free_var(&rounded); + return true; + } + + /* Check for negative input */ + if (rounded.sign == NUMERIC_NEG) + { + free_var(&rounded); + return false; + } + + /* + * For input like 10000000000, we must treat stripped digits as real. So + * the loop assumes there are weight+1 digits before the decimal point. + */ + weight = rounded.weight; + Assert(weight >= 0 && ndigits <= weight + 1); + + /* Construct the result */ + digits = rounded.digits; + val = digits[0]; + for (i = 1; i <= weight; i++) + { + if (unlikely(pg_mul_u64_overflow(val, NBASE, &val))) + { + free_var(&rounded); + return false; + } + + if (i < ndigits) + { + if (unlikely(pg_add_u64_overflow(val, digits[i], &val))) + { + free_var(&rounded); + return false; + } + } + } + + free_var(&rounded); + + *result = val; + + return true; +} + #ifdef HAVE_INT128 /* * Convert numeric to int128, rounding if needed. |