diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
new file mode 100644
index a83feea..c024fcf
--- 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;
 }
 
@@ -6934,6 +6885,7 @@ set_var_from_str(const char *str, const
 {
 	bool		have_dp = false;
 	int			i;
+	unsigned char *buf;
 	unsigned char *decdigits;
 	int			sign = NUMERIC_POS;
 	int			dweight = -1;
@@ -6970,7 +6922,14 @@ set_var_from_str(const char *str, const
 	if (!isdigit((unsigned char) *cp))
 		goto invalid_syntax;
 
-	decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
+	/*
+	 * Allocate a buffer for the decimal digit values, with space for leading
+	 * and trailing padding for digit alignment (DEC_DIGITS * 2).  This same
+	 * buffer will then be reused for the result's digit buffer, which might
+	 * later become a Numeric, so also include space for the numeric header.
+	 */
+	buf = (unsigned char *) palloc(NUMERIC_HDRSZ + strlen(cp) + DEC_DIGITS * 2);
+	decdigits = buf + NUMERIC_HDRSZ;
 
 	/* leading padding for digit alignment later */
 	memset(decdigits, 0, DEC_DIGITS);
@@ -7081,7 +7040,21 @@ set_var_from_str(const char *str, const
 	offset = (weight + 1) * DEC_DIGITS - (dweight + 1);
 	ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS;
 
-	alloc_var(dest, ndigits);
+	/*
+	 * Reuse the same buffer for the result to save a palloc() and pfree(),
+	 * making the digits start where they would in a Numeric result so that
+	 * make_result() doesn't need to move them if this variable is converted
+	 * to a Numeric.  Note that decdigits starts at an offset of NUMERIC_HDRSZ
+	 * into the buffer, so there's no danger of overwriting the decimal digits
+	 * as we produce the result digits.
+	 */
+	digitbuf_free(dest->buf);
+	dest->buf = (NumericDigit *) buf;
+	dest->digits = NUMERIC_CAN_BE_SHORT(dscale, weight) ?
+				   &((Numeric) (buf))->choice.n_short.n_data[0] :
+				   &((Numeric) (buf))->choice.n_long.n_data[0];
+	dest->digits[-1] = 0;			/* spare digit for rounding */
+	dest->ndigits = ndigits;
 	dest->sign = sign;
 	dest->weight = weight;
 	dest->dscale = dscale;
@@ -7104,8 +7077,6 @@ set_var_from_str(const char *str, const
 		i += DEC_DIGITS;
 	}
 
-	pfree(decdigits);
-
 	/* Strip any leading/trailing zeroes, and normalize weight if zero */
 	strip_var(dest);
 
@@ -7691,11 +7662,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 +7735,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 && result->choice.n_short.n_data != digits)
+			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 +7749,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 && result->choice.n_long.n_data != digits)
+			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 +7759,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 ||
