diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c new file mode 100644 index a83feea..c937eaf --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -474,8 +474,14 @@ static void dump_var(const char *str, Nu #define dump_var(s,v) #endif +/* + * Macros to allocate/free numeric digit buffers. Whenever we allocate a digit + * buffer, we give it an extra NUMERIC_HDRSZ (8 bytes) of space, so that the + * same memory block can be used by make_result() to construct a Numeric result + * from it, avoiding palloc/pfree overhead. + */ #define digitbuf_alloc(ndigits) \ - ((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit))) + ((NumericDigit *) palloc(NUMERIC_HDRSZ + (ndigits) * sizeof(NumericDigit))) #define digitbuf_free(buf) \ do { \ if ((buf) != NULL) \ @@ -783,8 +789,6 @@ numeric_in(PG_FUNCTION_ARGS) ereturn(escontext, (Datum) 0, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value overflows numeric format"))); - - free_var(&value); } PG_RETURN_NUMERIC(res); @@ -1141,8 +1145,6 @@ numeric_recv(PG_FUNCTION_ARGS) (void) apply_typmod_special(res, typmod, NULL); } - free_var(&value); - PG_RETURN_NUMERIC(res); } @@ -1305,8 +1307,6 @@ numeric (PG_FUNCTION_ARGS) (void) apply_typmod(&var, typmod, NULL); new = make_result(&var); - free_var(&var); - PG_RETURN_NUMERIC(new); } @@ -1566,7 +1566,6 @@ numeric_round(PG_FUNCTION_ARGS) */ res = make_result(&arg); - free_var(&arg); PG_RETURN_NUMERIC(res); } @@ -1615,7 +1614,6 @@ numeric_trunc(PG_FUNCTION_ARGS) */ res = make_result(&arg); - free_var(&arg); PG_RETURN_NUMERIC(res); } @@ -1642,7 +1640,6 @@ numeric_ceil(PG_FUNCTION_ARGS) ceil_var(&result, &result); res = make_result(&result); - free_var(&result); PG_RETURN_NUMERIC(res); } @@ -1670,7 +1667,6 @@ numeric_floor(PG_FUNCTION_ARGS) floor_var(&result, &result); res = make_result(&result); - free_var(&result); PG_RETURN_NUMERIC(res); } @@ -1793,7 +1789,13 @@ generate_series_step_numeric(PG_FUNCTION (fctx->step.sign == NUMERIC_NEG && cmp_var(&fctx->current, &fctx->stop) >= 0)) { - Numeric result = make_result(&fctx->current); + NumericVar result_var; + Numeric result; + + /* copy current and make result from copy */ + init_var(&result_var); + set_var_from_var(&fctx->current, &result_var); + result = make_result(&result_var); /* switch to memory context appropriate for iteration calculation */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -2898,8 +2900,6 @@ numeric_add_opt_error(Numeric num1, Nume res = make_result_opt_error(&result, have_error); - free_var(&result); - return res; } @@ -2976,8 +2976,6 @@ numeric_sub_opt_error(Numeric num1, Nume res = make_result_opt_error(&result, have_error); - free_var(&result); - return res; } @@ -3097,8 +3095,6 @@ numeric_mul_opt_error(Numeric num1, Nume res = make_result_opt_error(&result, have_error); - free_var(&result); - return res; } @@ -3232,8 +3228,6 @@ numeric_div_opt_error(Numeric num1, Nume res = make_result_opt_error(&result, have_error); - free_var(&result); - return res; } @@ -3321,8 +3315,6 @@ numeric_div_trunc(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3410,8 +3402,6 @@ numeric_mod_opt_error(Numeric num1, Nume res = make_result_opt_error(&result, NULL); - free_var(&result); - return res; } @@ -3443,8 +3433,6 @@ numeric_inc(PG_FUNCTION_ARGS) res = make_result(&arg); - free_var(&arg); - PG_RETURN_NUMERIC(res); } @@ -3537,8 +3525,6 @@ numeric_gcd(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3597,8 +3583,6 @@ numeric_lcm(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3648,9 +3632,6 @@ numeric_fac(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&fact); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3721,8 +3702,6 @@ numeric_sqrt(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3788,8 +3767,6 @@ numeric_exp(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3837,8 +3814,6 @@ numeric_ln(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -3908,8 +3883,6 @@ numeric_log(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -4096,8 +4069,6 @@ numeric_power(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -4183,7 +4154,6 @@ numeric_min_scale(PG_FUNCTION_ARGS) init_var_from_num(num, &arg); min_scale = get_min_scale(&arg); - free_var(&arg); PG_RETURN_INT32(min_scale); } @@ -4204,7 +4174,6 @@ numeric_trim_scale(PG_FUNCTION_ARGS) init_var_from_num(num, &result); result.dscale = get_min_scale(&result); res = make_result(&result); - free_var(&result); PG_RETURN_NUMERIC(res); } @@ -4229,8 +4198,6 @@ int64_to_numeric(int64 val) res = make_result(&result); - free_var(&result); - return res; } @@ -4318,8 +4285,6 @@ int64_div_fast_to_numeric(int64 val1, in res = make_result(&result); - free_var(&result); - return res; } @@ -4529,8 +4494,6 @@ float8_numeric(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -4623,8 +4586,6 @@ float4_numeric(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); } @@ -6005,8 +5966,6 @@ numeric_poly_sum(PG_FUNCTION_ARGS) res = make_result(&result); - free_var(&result); - PG_RETURN_NUMERIC(res); #else return numeric_sum(fcinfo); @@ -6035,8 +5994,6 @@ numeric_poly_avg(PG_FUNCTION_ARGS) countd = NumericGetDatum(int64_to_numeric(state->N)); sumd = NumericGetDatum(make_result(&result)); - free_var(&result); - PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd)); #else return numeric_avg(fcinfo); @@ -6073,7 +6030,6 @@ numeric_avg(PG_FUNCTION_ARGS) init_var(&sumX_var); accum_sum_final(&state->sumX, &sumX_var); sumX_datum = NumericGetDatum(make_result(&sumX_var)); - free_var(&sumX_var); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum)); } @@ -6105,7 +6061,6 @@ numeric_sum(PG_FUNCTION_ARGS) init_var(&sumX_var); accum_sum_final(&state->sumX, &sumX_var); result = make_result(&sumX_var); - free_var(&sumX_var); PG_RETURN_NUMERIC(result); } @@ -6198,10 +6153,6 @@ numeric_stddev_internal(NumericAggState res = make_result(&vsumX); } - free_var(&vNminus1); - free_var(&vsumX); - free_var(&vsumX2); - return res; } @@ -7691,11 +7642,19 @@ duplicate_numeric(Numeric num) /* * make_result_opt_error() - * - * Create the packed db numeric format in palloc()'d memory from - * a variable. This will handle NaN and Infinity cases. + * Create the packed db numeric format from a variable. This will handle NaN + * and Infinity cases. * * If "have_error" isn't NULL, on overflow *have_error is set to true and * NULL is returned. This is helpful when caller needs to handle errors. + * + * NOTE: This reuses the memory allocated for the variable's digit buffer, if + * possible. The variable must not be freed or used in any way afterwards (we + * may trample over its digit buffer). If the variable doesn't have an + * allocated digit buffer, new memory will be palloc'd and returned. This + * makes it safe to use with constant variables (const_one, const_nan, etc.), + * as well as variables initialized using init_var_from_num(), zeroed using + * zero_var(), etc. */ static Numeric make_result_opt_error(const NumericVar *var, bool *have_error) @@ -7756,7 +7715,9 @@ make_result_opt_error(const NumericVar * if (NUMERIC_CAN_BE_SHORT(var->dscale, weight)) { len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit); - result = (Numeric) palloc(len); + result = var->buf ? (Numeric) var->buf : (Numeric) palloc(len); + if (n > 0) + memmove(result->choice.n_short.n_data, digits, n * sizeof(NumericDigit)); SET_VARSIZE(result, len); result->choice.n_short.n_header = (sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) @@ -7768,7 +7729,9 @@ make_result_opt_error(const NumericVar * else { len = NUMERIC_HDRSZ + n * sizeof(NumericDigit); - result = (Numeric) palloc(len); + result = var->buf ? (Numeric) var->buf : (Numeric) palloc(len); + if (n > 0) + memmove(result->choice.n_long.n_data, digits, n * sizeof(NumericDigit)); SET_VARSIZE(result, len); result->choice.n_long.n_sign_dscale = sign | (var->dscale & NUMERIC_DSCALE_MASK); @@ -7776,8 +7739,6 @@ make_result_opt_error(const NumericVar * } Assert(NUMERIC_NDIGITS(result) == n); - if (n > 0) - memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit)); /* Check for overflow of int16 fields */ if (NUMERIC_WEIGHT(result) != weight ||