v9-0004-Add-safe-input-and-type-conversion-functions-for-.patch
text/x-patch
Filename: v9-0004-Add-safe-input-and-type-conversion-functions-for-.patch
Type: text/x-patch
Part: 3
Message:
Re: SQL/JSON features for v15
Patch
Format: format-patch
Series: patch v9-0004
Subject: Add safe input and type conversion functions for numeric
| File | + | − |
|---|---|---|
| src/backend/utils/adt/numeric.c | 156 | 74 |
| src/include/utils/numeric.h | 5 | 1 |
From 8365b41223945a3cfd9615fa33488d774f00e0f8 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 29 Aug 2022 23:55:52 +0300
Subject: [PATCH v9 4/9] Add safe input and type conversion functions for
numeric
---
src/backend/utils/adt/numeric.c | 230 ++++++++++++++++++++++----------
src/include/utils/numeric.h | 6 +-
2 files changed, 161 insertions(+), 75 deletions(-)
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 920a63b0081..951b2f43ab1 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -498,7 +498,7 @@ 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);
+ NumericVar *dest, bool *have_error);
static void set_var_from_num(Numeric value, 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 +512,8 @@ static Numeric duplicate_numeric(Numeric num);
static Numeric make_result(const NumericVar *var);
static Numeric make_result_opt_error(const NumericVar *var, bool *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, bool *error);
+static void apply_typmod_special(Numeric num, int32 typmod, bool *error);
static bool numericvar_to_int32(const NumericVar *var, int32 *result);
static bool numericvar_to_int64(const NumericVar *var, int64 *result);
@@ -607,21 +607,25 @@ static void accum_sum_combine(NumericSumAccum *accum, NumericSumAccum *accum2);
* ----------------------------------------------------------------------
*/
+/* Convenience macro: set *have_error flag (if provided) or throw error */
+#define RETURN_ERROR(throw_error, have_error) \
+do { \
+ if (have_error) { \
+ *(have_error) = true; \
+ return NULL; \
+ } else { \
+ throw_error; \
+ } \
+} while (0)
/*
* numeric_in() -
*
* Input function for numeric data type
*/
-Datum
-numeric_in(PG_FUNCTION_ARGS)
+Numeric
+numeric_in_opt_error(char *str, int32 typmod, bool *have_error)
{
- char *str = PG_GETARG_CSTRING(0);
-
-#ifdef NOT_USED
- Oid typelem = PG_GETARG_OID(1);
-#endif
- int32 typmod = PG_GETARG_INT32(2);
Numeric res;
const char *cp;
@@ -682,7 +686,10 @@ numeric_in(PG_FUNCTION_ARGS)
init_var(&value);
- cp = set_var_from_str(str, cp, &value);
+ cp = set_var_from_str(str, cp, &value, have_error);
+
+ if (!cp)
+ return NULL; /* error */
/*
* We duplicate a few lines of code here because we would like to
@@ -693,38 +700,59 @@ numeric_in(PG_FUNCTION_ARGS)
while (*cp)
{
if (!isspace((unsigned char) *cp))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "numeric", str)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "numeric", str))),
+ have_error);
cp++;
}
- apply_typmod(&value, typmod);
+ if (!apply_typmod(&value, typmod, have_error))
+ return NULL; /* error */
- res = make_result(&value);
+ res = make_result_opt_error(&value, have_error);
free_var(&value);
- PG_RETURN_NUMERIC(res);
+ return res;
}
/* Should be nothing left but spaces */
while (*cp)
{
if (!isspace((unsigned char) *cp))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "numeric", str)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "numeric", str))),
+ have_error);
+
cp++;
}
/* As above, throw any typmod error after finishing syntax check */
- apply_typmod_special(res, typmod);
+ apply_typmod_special(res, typmod, have_error);
- PG_RETURN_NUMERIC(res);
+ return res;
}
+/*
+ * numeric_in() -
+ *
+ * Input function for numeric data type
+ */
+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);
+
+ PG_RETURN_NUMERIC(numeric_in_opt_error(str, typmod, NULL));
+}
/*
* numeric_out() -
@@ -1058,7 +1086,7 @@ numeric_recv(PG_FUNCTION_ARGS)
{
trunc_var(&value, value.dscale);
- apply_typmod(&value, typmod);
+ apply_typmod(&value, typmod, NULL);
res = make_result(&value);
}
@@ -1067,7 +1095,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);
+ apply_typmod_special(res, typmod, NULL);
}
free_var(&value);
@@ -1180,7 +1208,7 @@ numeric (PG_FUNCTION_ARGS)
*/
if (NUMERIC_IS_SPECIAL(num))
{
- apply_typmod_special(num, typmod);
+ apply_typmod_special(num, typmod, NULL);
PG_RETURN_NUMERIC(duplicate_numeric(num));
}
@@ -1231,7 +1259,7 @@ numeric (PG_FUNCTION_ARGS)
init_var(&var);
set_var_from_num(num, &var);
- apply_typmod(&var, typmod);
+ apply_typmod(&var, typmod, NULL);
new = make_result(&var);
free_var(&var);
@@ -4308,15 +4336,20 @@ int8_numeric(PG_FUNCTION_ARGS)
}
-Datum
-numeric_int8(PG_FUNCTION_ARGS)
+int64
+numeric_int8_opt_error(Numeric num, bool *error)
{
- Numeric num = PG_GETARG_NUMERIC(0);
NumericVar x;
int64 result;
if (NUMERIC_IS_SPECIAL(num))
{
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
if (NUMERIC_IS_NAN(num))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -4331,13 +4364,26 @@ numeric_int8(PG_FUNCTION_ARGS)
init_var_from_num(num, &x);
if (!numericvar_to_int64(&x, &result))
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
+ }
- PG_RETURN_INT64(result);
+ return result;
}
+Datum
+numeric_int8(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(numeric_int8_opt_error(PG_GETARG_NUMERIC(0), NULL));
+}
Datum
int2_numeric(PG_FUNCTION_ARGS)
@@ -4347,17 +4393,20 @@ int2_numeric(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(int64_to_numeric(val));
}
-
-Datum
-numeric_int2(PG_FUNCTION_ARGS)
+int16
+numeric_int2_opt_error(Numeric num, bool *error)
{
- Numeric num = PG_GETARG_NUMERIC(0);
NumericVar x;
int64 val;
- int16 result;
if (NUMERIC_IS_SPECIAL(num))
{
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
if (NUMERIC_IS_NAN(num))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -4372,21 +4421,40 @@ numeric_int2(PG_FUNCTION_ARGS)
init_var_from_num(num, &x);
if (!numericvar_to_int64(&x, &val))
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ }
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ }
/* Down-convert to int2 */
- result = (int16) val;
-
- PG_RETURN_INT16(result);
+ return (int16) val;
}
+Datum
+numeric_int2(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT16(numeric_int2_opt_error(PG_GETARG_NUMERIC(0), NULL));
+}
Datum
float8_numeric(PG_FUNCTION_ARGS)
@@ -4412,7 +4480,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, NULL);
res = make_result(&result);
@@ -4505,7 +4573,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, NULL);
res = make_result(&result);
@@ -6811,7 +6879,8 @@ zero_var(NumericVar *var)
* reports. (Typically cp would be the same except advanced over spaces.)
*/
static const char *
-set_var_from_str(const char *str, const char *cp, NumericVar *dest)
+set_var_from_str(const char *str, const char *cp, NumericVar *dest,
+ bool *have_error)
{
bool have_dp = false;
int i;
@@ -6849,10 +6918,11 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
}
if (!isdigit((unsigned char) *cp))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "numeric", str)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "numeric", str))),
+ have_error);
decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
@@ -6873,10 +6943,11 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
else if (*cp == '.')
{
if (have_dp)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "numeric", str)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "numeric", str))),
+ have_error);
have_dp = true;
cp++;
}
@@ -6897,10 +6968,11 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
cp++;
exponent = strtol(cp, &endptr, 10);
if (endptr == cp)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "numeric", str)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "numeric", str))),
+ have_error);
cp = endptr;
/*
@@ -6912,9 +6984,10 @@ 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,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("value overflows numeric format")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value overflows numeric format"))),
+ have_error);
dweight += (int) exponent;
dscale -= (int) exponent;
if (dscale < 0)
@@ -7420,17 +7493,10 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
if (NUMERIC_WEIGHT(result) != weight ||
NUMERIC_DSCALE(result) != var->dscale)
{
- if (have_error)
- {
- *have_error = true;
- return NULL;
- }
- else
- {
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("value overflows numeric format")));
- }
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value overflows numeric format"))),
+ have_error);
}
dump_numeric("make_result()", result);
@@ -7456,8 +7522,8 @@ 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.
*/
-static void
-apply_typmod(NumericVar *var, int32 typmod)
+static bool
+apply_typmod(NumericVar *var, int32 typmod, bool *have_error)
{
int precision;
int scale;
@@ -7467,7 +7533,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,6 +7580,13 @@ apply_typmod(NumericVar *var, int32 typmod)
#error unsupported NBASE
#endif
if (ddigits > maxdigits)
+ {
+ if (have_error)
+ {
+ *have_error = true;
+ return false;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("numeric field overflow"),
@@ -7523,11 +7596,14 @@ apply_typmod(NumericVar *var, int32 typmod)
maxdigits ? "10^" : "",
maxdigits ? maxdigits : 1
)));
+ }
break;
}
ddigits -= DEC_DIGITS;
}
}
+
+ return true;
}
/*
@@ -7537,7 +7613,7 @@ apply_typmod(NumericVar *var, int32 typmod)
* For convenience of most callers, the value is presented in packed form.
*/
static void
-apply_typmod_special(Numeric num, int32 typmod)
+apply_typmod_special(Numeric num, int32 typmod, bool *have_error)
{
int precision;
int scale;
@@ -7560,6 +7636,12 @@ apply_typmod_special(Numeric num, int32 typmod)
precision = numeric_typmod_precision(typmod);
scale = numeric_typmod_scale(typmod);
+ if (have_error)
+ {
+ *have_error = true;
+ return;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("numeric field overflow"),
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7a..f32807cfdaa 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -85,6 +85,10 @@ extern Numeric numeric_div_opt_error(Numeric num1, Numeric num2,
bool *have_error);
extern Numeric numeric_mod_opt_error(Numeric num1, Numeric num2,
bool *have_error);
-extern int32 numeric_int4_opt_error(Numeric num, bool *error);
+extern int16 numeric_int2_opt_error(Numeric num, bool *have_error);
+extern int32 numeric_int4_opt_error(Numeric num, bool *have_error);
+extern int64 numeric_int8_opt_error(Numeric num, bool *have_error);
+extern Numeric numeric_in_opt_error(char *str, int32 typmod,
+ bool *have_error);
#endif /* _PG_NUMERIC_H_ */
--
2.17.1