aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-12-09 10:14:53 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2022-12-09 10:14:53 -0500
commitccff2d20ed9622815df2a7deffce8a7b14830965 (patch)
treeef8439338d811e3c3b4fd6413f42662c128c70c2 /src/backend/utils/adt/numeric.c
parent1939d26282b27b4b264c6930830a7991ed83917a (diff)
downloadpostgresql-ccff2d20ed9622815df2a7deffce8a7b14830965.tar.gz
postgresql-ccff2d20ed9622815df2a7deffce8a7b14830965.zip
Convert a few datatype input functions to use "soft" error reporting.
This patch converts the input functions for bool, int2, int4, int8, float4, float8, numeric, and contrib/cube to the new soft-error style. array_in and record_in are also converted. There's lots more to do, but this is enough to provide proof-of-concept that the soft-error API is usable, as well as reference examples for how to convert input functions. This patch is mostly by me, but it owes very substantial debt to earlier work by Nikita Glukhov, Andrew Dunstan, and Amul Sul. Thanks to Andres Freund for review. Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
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.",