aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r--src/backend/utils/adt/numeric.c98
1 files changed, 63 insertions, 35 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 7f0e93aa803..c024928bc8d 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -497,8 +497,9 @@ static void alloc_var(NumericVar *var, int ndigits);
static void free_var(NumericVar *var);
static void zero_var(NumericVar *var);
-static const char *set_var_from_str(const char *str, const char *cp,
- NumericVar *dest);
+static bool set_var_from_str(const char *str, const char *cp,
+ NumericVar *dest, const char **endptr,
+ Node *escontext);
static void set_var_from_num(Numeric num, NumericVar *dest);
static void init_var_from_num(Numeric num, NumericVar *dest);
static void set_var_from_var(const NumericVar *value, NumericVar *dest);
@@ -512,8 +513,8 @@ static Numeric duplicate_numeric(Numeric num);
static Numeric make_result(const NumericVar *var);
static Numeric make_result_opt_error(const NumericVar *var, bool *have_error);
-static void apply_typmod(NumericVar *var, int32 typmod);
-static void apply_typmod_special(Numeric num, int32 typmod);
+static bool apply_typmod(NumericVar *var, int32 typmod, Node *escontext);
+static bool apply_typmod_special(Numeric num, int32 typmod, Node *escontext);
static bool numericvar_to_int32(const NumericVar *var, int32 *result);
static bool numericvar_to_int64(const NumericVar *var, int64 *result);
@@ -617,11 +618,11 @@ Datum
numeric_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
Numeric res;
const char *cp;
@@ -679,10 +680,12 @@ numeric_in(PG_FUNCTION_ARGS)
* Use set_var_from_str() to parse a normal numeric value
*/
NumericVar value;
+ bool have_error;
init_var(&value);
- cp = set_var_from_str(str, cp, &value);
+ if (!set_var_from_str(str, cp, &value, &cp, escontext))
+ PG_RETURN_NULL();
/*
* We duplicate a few lines of code here because we would like to
@@ -693,16 +696,23 @@ numeric_in(PG_FUNCTION_ARGS)
while (*cp)
{
if (!isspace((unsigned char) *cp))
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"numeric", str)));
cp++;
}
- apply_typmod(&value, typmod);
+ if (!apply_typmod(&value, typmod, escontext))
+ PG_RETURN_NULL();
+
+ res = make_result_opt_error(&value, &have_error);
+
+ if (have_error)
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value overflows numeric format")));
- res = make_result(&value);
free_var(&value);
PG_RETURN_NUMERIC(res);
@@ -712,7 +722,7 @@ numeric_in(PG_FUNCTION_ARGS)
while (*cp)
{
if (!isspace((unsigned char) *cp))
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"numeric", str)));
@@ -720,7 +730,8 @@ numeric_in(PG_FUNCTION_ARGS)
}
/* As above, throw any typmod error after finishing syntax check */
- apply_typmod_special(res, typmod);
+ if (!apply_typmod_special(res, typmod, escontext))
+ PG_RETURN_NULL();
PG_RETURN_NUMERIC(res);
}
@@ -1058,7 +1069,7 @@ numeric_recv(PG_FUNCTION_ARGS)
{
trunc_var(&value, value.dscale);
- apply_typmod(&value, typmod);
+ (void) apply_typmod(&value, typmod, NULL);
res = make_result(&value);
}
@@ -1067,7 +1078,7 @@ numeric_recv(PG_FUNCTION_ARGS)
/* apply_typmod_special wants us to make the Numeric first */
res = make_result(&value);
- apply_typmod_special(res, typmod);
+ (void) apply_typmod_special(res, typmod, NULL);
}
free_var(&value);
@@ -1180,7 +1191,7 @@ numeric (PG_FUNCTION_ARGS)
*/
if (NUMERIC_IS_SPECIAL(num))
{
- apply_typmod_special(num, typmod);
+ (void) apply_typmod_special(num, typmod, NULL);
PG_RETURN_NUMERIC(duplicate_numeric(num));
}
@@ -1231,7 +1242,7 @@ numeric (PG_FUNCTION_ARGS)
init_var(&var);
set_var_from_num(num, &var);
- apply_typmod(&var, typmod);
+ (void) apply_typmod(&var, typmod, NULL);
new = make_result(&var);
free_var(&var);
@@ -4395,6 +4406,7 @@ float8_numeric(PG_FUNCTION_ARGS)
Numeric res;
NumericVar result;
char buf[DBL_DIG + 100];
+ const char *endptr;
if (isnan(val))
PG_RETURN_NUMERIC(make_result(&const_nan));
@@ -4412,7 +4424,7 @@ float8_numeric(PG_FUNCTION_ARGS)
init_var(&result);
/* Assume we need not worry about leading/trailing spaces */
- (void) set_var_from_str(buf, buf, &result);
+ (void) set_var_from_str(buf, buf, &result, &endptr, NULL);
res = make_result(&result);
@@ -4488,6 +4500,7 @@ float4_numeric(PG_FUNCTION_ARGS)
Numeric res;
NumericVar result;
char buf[FLT_DIG + 100];
+ const char *endptr;
if (isnan(val))
PG_RETURN_NUMERIC(make_result(&const_nan));
@@ -4505,7 +4518,7 @@ float4_numeric(PG_FUNCTION_ARGS)
init_var(&result);
/* Assume we need not worry about leading/trailing spaces */
- (void) set_var_from_str(buf, buf, &result);
+ (void) set_var_from_str(buf, buf, &result, &endptr, NULL);
res = make_result(&result);
@@ -6804,14 +6817,19 @@ zero_var(NumericVar *var)
* Parse a string and put the number into a variable
*
* This function does not handle leading or trailing spaces. It returns
- * the end+1 position parsed, so that caller can check for trailing
- * spaces/garbage if deemed necessary.
+ * the end+1 position parsed into *endptr, so that caller can check for
+ * trailing spaces/garbage if deemed necessary.
*
* cp is the place to actually start parsing; str is what to use in error
* reports. (Typically cp would be the same except advanced over spaces.)
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
-static const char *
-set_var_from_str(const char *str, const char *cp, NumericVar *dest)
+static bool
+set_var_from_str(const char *str, const char *cp,
+ NumericVar *dest, const char **endptr,
+ Node *escontext)
{
bool have_dp = false;
int i;
@@ -6849,7 +6867,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
}
if (!isdigit((unsigned char) *cp))
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"numeric", str)));
@@ -6873,7 +6891,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
else if (*cp == '.')
{
if (have_dp)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"numeric", str)));
@@ -6897,7 +6915,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
cp++;
exponent = strtol(cp, &endptr, 10);
if (endptr == cp)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"numeric", str)));
@@ -6912,7 +6930,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
* for consistency use the same ereport errcode/text as make_result().
*/
if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value overflows numeric format")));
dweight += (int) exponent;
@@ -6963,7 +6981,9 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
strip_var(dest);
/* Return end+1 position for caller */
- return cp;
+ *endptr = cp;
+
+ return true;
}
@@ -7455,9 +7475,12 @@ make_result(const NumericVar *var)
*
* Do bounds checking and rounding according to the specified typmod.
* Note that this is only applied to normal finite values.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
-static void
-apply_typmod(NumericVar *var, int32 typmod)
+static bool
+apply_typmod(NumericVar *var, int32 typmod, Node *escontext)
{
int precision;
int scale;
@@ -7467,7 +7490,7 @@ apply_typmod(NumericVar *var, int32 typmod)
/* Do nothing if we have an invalid typmod */
if (!is_valid_numeric_typmod(typmod))
- return;
+ return true;
precision = numeric_typmod_precision(typmod);
scale = numeric_typmod_scale(typmod);
@@ -7514,7 +7537,7 @@ apply_typmod(NumericVar *var, int32 typmod)
#error unsupported NBASE
#endif
if (ddigits > maxdigits)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("numeric field overflow"),
errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.",
@@ -7528,6 +7551,8 @@ apply_typmod(NumericVar *var, int32 typmod)
ddigits -= DEC_DIGITS;
}
}
+
+ return true;
}
/*
@@ -7535,9 +7560,12 @@ apply_typmod(NumericVar *var, int32 typmod)
*
* Do bounds checking according to the specified typmod, for an Inf or NaN.
* For convenience of most callers, the value is presented in packed form.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
-static void
-apply_typmod_special(Numeric num, int32 typmod)
+static bool
+apply_typmod_special(Numeric num, int32 typmod, Node *escontext)
{
int precision;
int scale;
@@ -7551,16 +7579,16 @@ apply_typmod_special(Numeric num, int32 typmod)
* any finite number of digits.
*/
if (NUMERIC_IS_NAN(num))
- return;
+ return true;
/* Do nothing if we have a default typmod (-1) */
if (!is_valid_numeric_typmod(typmod))
- return;
+ return true;
precision = numeric_typmod_precision(typmod);
scale = numeric_typmod_scale(typmod);
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("numeric field overflow"),
errdetail("A field with precision %d, scale %d cannot hold an infinite value.",