From 8365b41223945a3cfd9615fa33488d774f00e0f8 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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