Subject: Safer and faster get_attstatsslot()

From: Tom Lane <[email-masked]>
Date: Thu, 11 May 2017 12:41:02 -0400

Monday's round of security patches was a lot more exciting than I would
have liked, because code that worked fine for Peter and me failed
erratically in the buildfarm.  What eventually emerged was that I'd added
some missing free_attstatsslot() calls in rangetypes_selfuncs.c, and
naively copied the first argument (atttype) from the matching
get_attstatsslot() calls.  One of those atttype values was in fact
wrong for the slot in question; this had been missed for years because
get_attstatsslot() doesn't actually do anything with that argument.
I think that at one point we had, or at least in the original conception
intended to have, an Assert that the atttype matched the actual stats
array element type found in the pg_statistic row; but we had to remove
it because in some cases the type in pg_statistic is only
binary-compatible with the datatype the applied operator is expecting.

So the existing API for get_attstatsslot()/free_attstatsslot() is just
seriously bug-prone.  It would be better if the caller did not have
to supply any type information; indeed really what we'd want is for
get_attstatsslot() to pass back the actual type it found in pg_statistic.

I also realized as I looked at the code that it's exceedingly inefficient
if the array element type is pass-by-reference --- then it'll incur a
separate palloc, copy, and pfree for each element.  We'd be a lot better
off to copy the stats array as a whole, especially since that would come
for free in the probably-common case that the array has to be detoasted.
This code was written with very small stats targets in mind, like about
10, and it just doesn't look very good when you're imagining 1000 or more
entries in the stats array.

So attached is a proposed redesign that makes the API for
get_attstatsslot()/free_attstatsslot() simpler and hopefully more
foolproof.  I've not made any particular attempt to performance-test
it, but it really ought to be a significant win for pass-by-ref element
types.  It will add an array-copying step that wasn't there before when
the element type is pass-by-value and the array isn't toasted, but that
seems like an acceptable price.

BTW, this patch makes the skewColType and skewColTypmod fields of Hash
plan nodes unnecessary, as the only reason for them was to satisfy
get_attstatsslot().  I didn't remove them here but it would make sense
to do so.

Comments?  Is this something that'd be OK to push now, or had I better
sit on it till v11?  Being freshly burned, I kinda want to fix it now,
but I recognize that my judgment may be colored by that.

			regards, tom lane

diff --git a/contrib/intarray/_int_selfuncs.c b/contrib/intarray/_int_selfuncs.c
index 9b4a22f..3d92025 100644
*** a/contrib/intarray/_int_selfuncs.c
--- b/contrib/intarray/_int_selfuncs.c
*************** _int_matchsel(PG_FUNCTION_ARGS)
*** 136,146 ****
  	int			nmcelems = 0;
  	float4		minfreq = 0.0;
  	float4		nullfrac = 0.0;
! 	Form_pg_statistic stats;
! 	Datum	   *values = NULL;
! 	int			nvalues = 0;
! 	float4	   *numbers = NULL;
! 	int			nnumbers = 0;
  
  	/*
  	 * If expression is not "variable @@ something" or "something @@ variable"
--- 136,142 ----
  	int			nmcelems = 0;
  	float4		minfreq = 0.0;
  	float4		nullfrac = 0.0;
! 	AttStatsSlot sslot;
  
  	/*
  	 * If expression is not "variable @@ something" or "something @@ variable"
*************** _int_matchsel(PG_FUNCTION_ARGS)
*** 193,198 ****
--- 189,196 ----
  	 */
  	if (HeapTupleIsValid(vardata.statsTuple))
  	{
+ 		Form_pg_statistic stats;
+ 
  		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
  		nullfrac = stats->stanullfrac;
  
*************** _int_matchsel(PG_FUNCTION_ARGS)
*** 200,228 ****
  		 * For an int4 array, the default array type analyze function will
  		 * collect a Most Common Elements list, which is an array of int4s.
  		 */
! 		if (get_attstatsslot(vardata.statsTuple,
! 							 INT4OID, -1,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 NULL,
! 							 &values, &nvalues,
! 							 &numbers, &nnumbers))
  		{
  			/*
  			 * There should be three more Numbers than Values, because the
  			 * last three (for intarray) cells are taken for minimal, maximal
  			 * and nulls frequency. Punt if not.
  			 */
! 			if (nnumbers == nvalues + 3)
  			{
  				/* Grab the lowest frequency. */
! 				minfreq = numbers[nnumbers - (nnumbers - nvalues)];
  
! 				mcelems = values;
! 				mcefreqs = numbers;
! 				nmcelems = nvalues;
  			}
  		}
  	}
  
  	/* Process the logical expression in the query, using the stats */
  	selec = int_query_opr_selec(GETQUERY(query) + query->size - 1,
--- 198,227 ----
  		 * For an int4 array, the default array type analyze function will
  		 * collect a Most Common Elements list, which is an array of int4s.
  		 */
! 		if (get_attstatsslot(&sslot, vardata.statsTuple,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  		{
+ 			Assert(sslot.valuetype == INT4OID);
+ 
  			/*
  			 * There should be three more Numbers than Values, because the
  			 * last three (for intarray) cells are taken for minimal, maximal
  			 * and nulls frequency. Punt if not.
  			 */
! 			if (sslot.nnumbers == sslot.nvalues + 3)
  			{
  				/* Grab the lowest frequency. */
! 				minfreq = sslot.numbers[sslot.nnumbers - (sslot.nnumbers - sslot.nvalues)];
  
! 				mcelems = sslot.values;
! 				mcefreqs = sslot.numbers;
! 				nmcelems = sslot.nvalues;
  			}
  		}
  	}
+ 	else
+ 		memset(&sslot, 0, sizeof(sslot));
  
  	/* Process the logical expression in the query, using the stats */
  	selec = int_query_opr_selec(GETQUERY(query) + query->size - 1,
*************** _int_matchsel(PG_FUNCTION_ARGS)
*** 231,237 ****
  	/* MCE stats count only non-null rows, so adjust for null rows. */
  	selec *= (1.0 - nullfrac);
  
! 	free_attstatsslot(INT4OID, values, nvalues, numbers, nnumbers);
  	ReleaseVariableStats(vardata);
  
  	CLAMP_PROBABILITY(selec);
--- 230,236 ----
  	/* MCE stats count only non-null rows, so adjust for null rows. */
  	selec *= (1.0 - nullfrac);
  
! 	free_attstatsslot(&sslot);
  	ReleaseVariableStats(vardata);
  
  	CLAMP_PROBABILITY(selec);
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index cfc6b96..d9789d0 100644
*** a/src/backend/executor/nodeHash.c
--- b/src/backend/executor/nodeHash.c
*************** static void
*** 1283,1292 ****
  ExecHashBuildSkewHash(HashJoinTable hashtable, Hash *node, int mcvsToUse)
  {
  	HeapTupleData *statsTuple;
! 	Datum	   *values;
! 	int			nvalues;
! 	float4	   *numbers;
! 	int			nnumbers;
  
  	/* Do nothing if planner didn't identify the outer relation's join key */
  	if (!OidIsValid(node->skewTable))
--- 1283,1289 ----
  ExecHashBuildSkewHash(HashJoinTable hashtable, Hash *node, int mcvsToUse)
  {
  	HeapTupleData *statsTuple;
! 	AttStatsSlot sslot;
  
  	/* Do nothing if planner didn't identify the outer relation's join key */
  	if (!OidIsValid(node->skewTable))
*************** ExecHashBuildSkewHash(HashJoinTable hash
*** 1305,1323 ****
  	if (!HeapTupleIsValid(statsTuple))
  		return;
  
! 	if (get_attstatsslot(statsTuple, node->skewColType, node->skewColTypmod,
  						 STATISTIC_KIND_MCV, InvalidOid,
! 						 NULL,
! 						 &values, &nvalues,
! 						 &numbers, &nnumbers))
  	{
  		double		frac;
  		int			nbuckets;
  		FmgrInfo   *hashfunctions;
  		int			i;
  
! 		if (mcvsToUse > nvalues)
! 			mcvsToUse = nvalues;
  
  		/*
  		 * Calculate the expected fraction of outer relation that will
--- 1302,1318 ----
  	if (!HeapTupleIsValid(statsTuple))
  		return;
  
! 	if (get_attstatsslot(&sslot, statsTuple,
  						 STATISTIC_KIND_MCV, InvalidOid,
! 						 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  	{
  		double		frac;
  		int			nbuckets;
  		FmgrInfo   *hashfunctions;
  		int			i;
  
! 		if (mcvsToUse > sslot.nvalues)
! 			mcvsToUse = sslot.nvalues;
  
  		/*
  		 * Calculate the expected fraction of outer relation that will
*************** ExecHashBuildSkewHash(HashJoinTable hash
*** 1326,1336 ****
  		 */
  		frac = 0;
  		for (i = 0; i < mcvsToUse; i++)
! 			frac += numbers[i];
  		if (frac < SKEW_MIN_OUTER_FRACTION)
  		{
! 			free_attstatsslot(node->skewColType,
! 							  values, nvalues, numbers, nnumbers);
  			ReleaseSysCache(statsTuple);
  			return;
  		}
--- 1321,1330 ----
  		 */
  		frac = 0;
  		for (i = 0; i < mcvsToUse; i++)
! 			frac += sslot.numbers[i];
  		if (frac < SKEW_MIN_OUTER_FRACTION)
  		{
! 			free_attstatsslot(&sslot);
  			ReleaseSysCache(statsTuple);
  			return;
  		}
*************** ExecHashBuildSkewHash(HashJoinTable hash
*** 1392,1398 ****
  			int			bucket;
  
  			hashvalue = DatumGetUInt32(FunctionCall1(&hashfunctions[0],
! 													 values[i]));
  
  			/*
  			 * While we have not hit a hole in the hashtable and have not hit
--- 1386,1392 ----
  			int			bucket;
  
  			hashvalue = DatumGetUInt32(FunctionCall1(&hashfunctions[0],
! 													 sslot.values[i]));
  
  			/*
  			 * While we have not hit a hole in the hashtable and have not hit
*************** ExecHashBuildSkewHash(HashJoinTable hash
*** 1426,1433 ****
  				hashtable->spacePeak = hashtable->spaceUsed;
  		}
  
! 		free_attstatsslot(node->skewColType,
! 						  values, nvalues, numbers, nnumbers);
  	}
  
  	ReleaseSysCache(statsTuple);
--- 1420,1426 ----
  				hashtable->spacePeak = hashtable->spaceUsed;
  		}
  
! 		free_attstatsslot(&sslot);
  	}
  
  	ReleaseSysCache(statsTuple);
diff --git a/src/backend/tsearch/ts_selfuncs.c b/src/backend/tsearch/ts_selfuncs.c
index 904d884..046f543 100644
*** a/src/backend/tsearch/ts_selfuncs.c
--- b/src/backend/tsearch/ts_selfuncs.c
*************** tsquerysel(VariableStatData *vardata, Da
*** 163,190 ****
  	if (HeapTupleIsValid(vardata->statsTuple))
  	{
  		Form_pg_statistic stats;
! 		Datum	   *values;
! 		int			nvalues;
! 		float4	   *numbers;
! 		int			nnumbers;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  
  		/* MCELEM will be an array of TEXT elements for a tsvector column */
! 		if (get_attstatsslot(vardata->statsTuple,
! 							 TEXTOID, -1,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 NULL,
! 							 &values, &nvalues,
! 							 &numbers, &nnumbers))
  		{
  			/*
  			 * There is a most-common-elements slot for the tsvector Var, so
  			 * use that.
  			 */
! 			selec = mcelem_tsquery_selec(query, values, nvalues,
! 										 numbers, nnumbers);
! 			free_attstatsslot(TEXTOID, values, nvalues, numbers, nnumbers);
  		}
  		else
  		{
--- 163,184 ----
  	if (HeapTupleIsValid(vardata->statsTuple))
  	{
  		Form_pg_statistic stats;
! 		AttStatsSlot sslot;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  
  		/* MCELEM will be an array of TEXT elements for a tsvector column */
! 		if (get_attstatsslot(&sslot, vardata->statsTuple,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  		{
  			/*
  			 * There is a most-common-elements slot for the tsvector Var, so
  			 * use that.
  			 */
! 			selec = mcelem_tsquery_selec(query, sslot.values, sslot.nvalues,
! 										 sslot.numbers, sslot.nnumbers);
! 			free_attstatsslot(&sslot);
  		}
  		else
  		{
diff --git a/src/backend/utils/adt/array_selfuncs.c b/src/backend/utils/adt/array_selfuncs.c
index cfaf873..3ae6018 100644
*** a/src/backend/utils/adt/array_selfuncs.c
--- b/src/backend/utils/adt/array_selfuncs.c
*************** scalararraysel_containment(PlannerInfo *
*** 137,171 ****
  		statistic_proc_security_check(&vardata, cmpfunc->fn_oid))
  	{
  		Form_pg_statistic stats;
! 		Datum	   *values;
! 		int			nvalues;
! 		float4	   *numbers;
! 		int			nnumbers;
! 		float4	   *hist;
! 		int			nhist;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
  
  		/* MCELEM will be an array of same type as element */
! 		if (get_attstatsslot(vardata.statsTuple,
! 							 elemtype, vardata.atttypmod,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 NULL,
! 							 &values, &nvalues,
! 							 &numbers, &nnumbers))
  		{
  			/* For ALL case, also get histogram of distinct-element counts */
  			if (useOr ||
! 				!get_attstatsslot(vardata.statsTuple,
! 								  elemtype, vardata.atttypmod,
  								  STATISTIC_KIND_DECHIST, InvalidOid,
! 								  NULL,
! 								  NULL, NULL,
! 								  &hist, &nhist))
! 			{
! 				hist = NULL;
! 				nhist = 0;
! 			}
  
  			/*
  			 * For = ANY, estimate as var @> ARRAY[const].
--- 137,158 ----
  		statistic_proc_security_check(&vardata, cmpfunc->fn_oid))
  	{
  		Form_pg_statistic stats;
! 		AttStatsSlot sslot;
! 		AttStatsSlot hslot;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
  
  		/* MCELEM will be an array of same type as element */
! 		if (get_attstatsslot(&sslot, vardata.statsTuple,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  		{
  			/* For ALL case, also get histogram of distinct-element counts */
  			if (useOr ||
! 				!get_attstatsslot(&hslot, vardata.statsTuple,
  								  STATISTIC_KIND_DECHIST, InvalidOid,
! 								  ATTSTATSSLOT_NUMBERS))
! 				memset(&hslot, 0, sizeof(hslot));
  
  			/*
  			 * For = ANY, estimate as var @> ARRAY[const].
*************** scalararraysel_containment(PlannerInfo *
*** 173,194 ****
  			 * For = ALL, estimate as var <@ ARRAY[const].
  			 */
  			if (useOr)
! 				selec = mcelem_array_contain_overlap_selec(values, nvalues,
! 														   numbers, nnumbers,
  														   &constval, 1,
  													   OID_ARRAY_CONTAINS_OP,
  														   cmpfunc);
  			else
! 				selec = mcelem_array_contained_selec(values, nvalues,
! 													 numbers, nnumbers,
  													 &constval, 1,
! 													 hist, nhist,
  													 OID_ARRAY_CONTAINED_OP,
  													 cmpfunc);
  
! 			if (hist)
! 				free_attstatsslot(elemtype, NULL, 0, hist, nhist);
! 			free_attstatsslot(elemtype, values, nvalues, numbers, nnumbers);
  		}
  		else
  		{
--- 160,185 ----
  			 * For = ALL, estimate as var <@ ARRAY[const].
  			 */
  			if (useOr)
! 				selec = mcelem_array_contain_overlap_selec(sslot.values,
! 														   sslot.nvalues,
! 														   sslot.numbers,
! 														   sslot.nnumbers,
  														   &constval, 1,
  													   OID_ARRAY_CONTAINS_OP,
  														   cmpfunc);
  			else
! 				selec = mcelem_array_contained_selec(sslot.values,
! 													 sslot.nvalues,
! 													 sslot.numbers,
! 													 sslot.nnumbers,
  													 &constval, 1,
! 													 hslot.numbers,
! 													 hslot.nnumbers,
  													 OID_ARRAY_CONTAINED_OP,
  													 cmpfunc);
  
! 			free_attstatsslot(&hslot);
! 			free_attstatsslot(&sslot);
  		}
  		else
  		{
*************** calc_arraycontsel(VariableStatData *vard
*** 369,417 ****
  		statistic_proc_security_check(vardata, cmpfunc->fn_oid))
  	{
  		Form_pg_statistic stats;
! 		Datum	   *values;
! 		int			nvalues;
! 		float4	   *numbers;
! 		int			nnumbers;
! 		float4	   *hist;
! 		int			nhist;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  
  		/* MCELEM will be an array of same type as column */
! 		if (get_attstatsslot(vardata->statsTuple,
! 							 elemtype, vardata->atttypmod,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 NULL,
! 							 &values, &nvalues,
! 							 &numbers, &nnumbers))
  		{
  			/*
  			 * For "array <@ const" case we also need histogram of distinct
  			 * element counts.
  			 */
  			if (operator != OID_ARRAY_CONTAINED_OP ||
! 				!get_attstatsslot(vardata->statsTuple,
! 								  elemtype, vardata->atttypmod,
  								  STATISTIC_KIND_DECHIST, InvalidOid,
! 								  NULL,
! 								  NULL, NULL,
! 								  &hist, &nhist))
! 			{
! 				hist = NULL;
! 				nhist = 0;
! 			}
  
  			/* Use the most-common-elements slot for the array Var. */
  			selec = mcelem_array_selec(array, typentry,
! 									   values, nvalues,
! 									   numbers, nnumbers,
! 									   hist, nhist,
  									   operator, cmpfunc);
  
! 			if (hist)
! 				free_attstatsslot(elemtype, NULL, 0, hist, nhist);
! 			free_attstatsslot(elemtype, values, nvalues, numbers, nnumbers);
  		}
  		else
  		{
--- 360,394 ----
  		statistic_proc_security_check(vardata, cmpfunc->fn_oid))
  	{
  		Form_pg_statistic stats;
! 		AttStatsSlot sslot;
! 		AttStatsSlot hslot;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  
  		/* MCELEM will be an array of same type as column */
! 		if (get_attstatsslot(&sslot, vardata->statsTuple,
  							 STATISTIC_KIND_MCELEM, InvalidOid,
! 							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  		{
  			/*
  			 * For "array <@ const" case we also need histogram of distinct
  			 * element counts.
  			 */
  			if (operator != OID_ARRAY_CONTAINED_OP ||
! 				!get_attstatsslot(&hslot, vardata->statsTuple,
  								  STATISTIC_KIND_DECHIST, InvalidOid,
! 								  ATTSTATSSLOT_NUMBERS))
! 				memset(&hslot, 0, sizeof(hslot));
  
  			/* Use the most-common-elements slot for the array Var. */
  			selec = mcelem_array_selec(array, typentry,
! 									   sslot.values, sslot.nvalues,
! 									   sslot.numbers, sslot.nnumbers,
! 									   hslot.numbers, hslot.nnumbers,
  									   operator, cmpfunc);
  
! 			free_attstatsslot(&hslot);
! 			free_attstatsslot(&sslot);
  		}
  		else
  		{
diff --git a/src/backend/utils/adt/network_selfuncs.c b/src/backend/utils/adt/network_selfuncs.c
index bcdd902..1d29ecd 100644
*** a/src/backend/utils/adt/network_selfuncs.c
--- b/src/backend/utils/adt/network_selfuncs.c
*************** networksel(PG_FUNCTION_ARGS)
*** 88,97 ****
  	Selectivity selec,
  				mcv_selec,
  				non_mcv_selec;
! 	Datum		constvalue,
! 			   *hist_values;
! 	int			hist_nvalues;
  	Form_pg_statistic stats;
  	double		sumcommon,
  				nullfrac;
  	FmgrInfo	proc;
--- 88,96 ----
  	Selectivity selec,
  				mcv_selec,
  				non_mcv_selec;
! 	Datum		constvalue;
  	Form_pg_statistic stats;
+ 	AttStatsSlot hslot;
  	double		sumcommon,
  				nullfrac;
  	FmgrInfo	proc;
*************** networksel(PG_FUNCTION_ARGS)
*** 146,167 ****
  	 * non-MCV population that satisfies the clause.  If we don't, apply the
  	 * default selectivity to that population.
  	 */
! 	if (get_attstatsslot(vardata.statsTuple,
! 						 vardata.atttype, vardata.atttypmod,
  						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 						 NULL,
! 						 &hist_values, &hist_nvalues,
! 						 NULL, NULL))
  	{
  		int			opr_codenum = inet_opr_codenum(operator);
  
  		/* Commute if needed, so we can consider histogram to be on the left */
  		if (!varonleft)
  			opr_codenum = -opr_codenum;
! 		non_mcv_selec = inet_hist_value_sel(hist_values, hist_nvalues,
  											constvalue, opr_codenum);
  
! 		free_attstatsslot(vardata.atttype, hist_values, hist_nvalues, NULL, 0);
  	}
  	else
  		non_mcv_selec = DEFAULT_SEL(operator);
--- 145,163 ----
  	 * non-MCV population that satisfies the clause.  If we don't, apply the
  	 * default selectivity to that population.
  	 */
! 	if (get_attstatsslot(&hslot, vardata.statsTuple,
  						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 						 ATTSTATSSLOT_VALUES))
  	{
  		int			opr_codenum = inet_opr_codenum(operator);
  
  		/* Commute if needed, so we can consider histogram to be on the left */
  		if (!varonleft)
  			opr_codenum = -opr_codenum;
! 		non_mcv_selec = inet_hist_value_sel(hslot.values, hslot.nvalues,
  											constvalue, opr_codenum);
  
! 		free_attstatsslot(&hslot);
  	}
  	else
  		non_mcv_selec = DEFAULT_SEL(operator);
*************** networkjoinsel_inner(Oid operator,
*** 277,318 ****
  				hist1_exists = false,
  				hist2_exists = false;
  	int			opr_codenum;
! 	int			mcv1_nvalues,
! 				mcv2_nvalues,
! 				mcv1_nnumbers,
! 				mcv2_nnumbers,
! 				hist1_nvalues,
! 				hist2_nvalues,
! 				mcv1_length = 0,
  				mcv2_length = 0;
! 	Datum	   *mcv1_values,
! 			   *mcv2_values,
! 			   *hist1_values,
! 			   *hist2_values;
! 	float4	   *mcv1_numbers,
! 			   *mcv2_numbers;
  
  	if (HeapTupleIsValid(vardata1->statsTuple))
  	{
  		stats = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		nullfrac1 = stats->stanullfrac;
  
! 		mcv1_exists = get_attstatsslot(vardata1->statsTuple,
! 									   vardata1->atttype, vardata1->atttypmod,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 									   NULL,
! 									   &mcv1_values, &mcv1_nvalues,
! 									   &mcv1_numbers, &mcv1_nnumbers);
! 		hist1_exists = get_attstatsslot(vardata1->statsTuple,
! 									  vardata1->atttype, vardata1->atttypmod,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										NULL,
! 										&hist1_values, &hist1_nvalues,
! 										NULL, NULL);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv1_length = Min(mcv1_nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv1_exists)
! 			sumcommon1 = mcv_population(mcv1_numbers, mcv1_length);
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple))
--- 273,305 ----
  				hist1_exists = false,
  				hist2_exists = false;
  	int			opr_codenum;
! 	int			mcv1_length = 0,
  				mcv2_length = 0;
! 	AttStatsSlot mcv1_slot;
! 	AttStatsSlot mcv2_slot;
! 	AttStatsSlot hist1_slot;
! 	AttStatsSlot hist2_slot;
  
  	if (HeapTupleIsValid(vardata1->statsTuple))
  	{
  		stats = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		nullfrac1 = stats->stanullfrac;
  
! 		mcv1_exists = get_attstatsslot(&mcv1_slot, vardata1->statsTuple,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
! 		hist1_exists = get_attstatsslot(&hist1_slot, vardata1->statsTuple,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										ATTSTATSSLOT_VALUES);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv1_length = Min(mcv1_slot.nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv1_exists)
! 			sumcommon1 = mcv_population(mcv1_slot.numbers, mcv1_length);
! 	}
! 	else
! 	{
! 		memset(&mcv1_slot, 0, sizeof(mcv1_slot));
! 		memset(&hist1_slot, 0, sizeof(hist1_slot));
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple))
*************** networkjoinsel_inner(Oid operator,
*** 320,341 ****
  		stats = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
  		nullfrac2 = stats->stanullfrac;
  
! 		mcv2_exists = get_attstatsslot(vardata2->statsTuple,
! 									   vardata2->atttype, vardata2->atttypmod,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 									   NULL,
! 									   &mcv2_values, &mcv2_nvalues,
! 									   &mcv2_numbers, &mcv2_nnumbers);
! 		hist2_exists = get_attstatsslot(vardata2->statsTuple,
! 									  vardata2->atttype, vardata2->atttypmod,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										NULL,
! 										&hist2_values, &hist2_nvalues,
! 										NULL, NULL);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv2_length = Min(mcv2_nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv2_exists)
! 			sumcommon2 = mcv_population(mcv2_numbers, mcv2_length);
  	}
  
  	opr_codenum = inet_opr_codenum(operator);
--- 307,327 ----
  		stats = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
  		nullfrac2 = stats->stanullfrac;
  
! 		mcv2_exists = get_attstatsslot(&mcv2_slot, vardata2->statsTuple,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
! 		hist2_exists = get_attstatsslot(&hist2_slot, vardata2->statsTuple,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										ATTSTATSSLOT_VALUES);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv2_length = Min(mcv2_slot.nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv2_exists)
! 			sumcommon2 = mcv_population(mcv2_slot.numbers, mcv2_length);
! 	}
! 	else
! 	{
! 		memset(&mcv2_slot, 0, sizeof(mcv2_slot));
! 		memset(&hist2_slot, 0, sizeof(hist2_slot));
  	}
  
  	opr_codenum = inet_opr_codenum(operator);
*************** networkjoinsel_inner(Oid operator,
*** 344,351 ****
  	 * Calculate selectivity for MCV vs MCV matches.
  	 */
  	if (mcv1_exists && mcv2_exists)
! 		selec += inet_mcv_join_sel(mcv1_values, mcv1_numbers, mcv1_length,
! 								   mcv2_values, mcv2_numbers, mcv2_length,
  								   operator);
  
  	/*
--- 330,339 ----
  	 * Calculate selectivity for MCV vs MCV matches.
  	 */
  	if (mcv1_exists && mcv2_exists)
! 		selec += inet_mcv_join_sel(mcv1_slot.values, mcv1_slot.numbers,
! 								   mcv1_length,
! 								   mcv2_slot.values, mcv2_slot.numbers,
! 								   mcv2_length,
  								   operator);
  
  	/*
*************** networkjoinsel_inner(Oid operator,
*** 355,367 ****
  	 */
  	if (mcv1_exists && hist2_exists)
  		selec += (1.0 - nullfrac2 - sumcommon2) *
! 			inet_mcv_hist_sel(mcv1_values, mcv1_numbers, mcv1_length,
! 							  hist2_values, hist2_nvalues,
  							  opr_codenum);
  	if (mcv2_exists && hist1_exists)
  		selec += (1.0 - nullfrac1 - sumcommon1) *
! 			inet_mcv_hist_sel(mcv2_values, mcv2_numbers, mcv2_length,
! 							  hist1_values, hist1_nvalues,
  							  -opr_codenum);
  
  	/*
--- 343,355 ----
  	 */
  	if (mcv1_exists && hist2_exists)
  		selec += (1.0 - nullfrac2 - sumcommon2) *
! 			inet_mcv_hist_sel(mcv1_slot.values, mcv1_slot.numbers, mcv1_length,
! 							  hist2_slot.values, hist2_slot.nvalues,
  							  opr_codenum);
  	if (mcv2_exists && hist1_exists)
  		selec += (1.0 - nullfrac1 - sumcommon1) *
! 			inet_mcv_hist_sel(mcv2_slot.values, mcv2_slot.numbers, mcv2_length,
! 							  hist1_slot.values, hist1_slot.nvalues,
  							  -opr_codenum);
  
  	/*
*************** networkjoinsel_inner(Oid operator,
*** 371,378 ****
  	if (hist1_exists && hist2_exists)
  		selec += (1.0 - nullfrac1 - sumcommon1) *
  			(1.0 - nullfrac2 - sumcommon2) *
! 			inet_hist_inclusion_join_sel(hist1_values, hist1_nvalues,
! 										 hist2_values, hist2_nvalues,
  										 opr_codenum);
  
  	/*
--- 359,366 ----
  	if (hist1_exists && hist2_exists)
  		selec += (1.0 - nullfrac1 - sumcommon1) *
  			(1.0 - nullfrac2 - sumcommon2) *
! 			inet_hist_inclusion_join_sel(hist1_slot.values, hist1_slot.nvalues,
! 									   hist2_slot.values, hist2_slot.nvalues,
  										 opr_codenum);
  
  	/*
*************** networkjoinsel_inner(Oid operator,
*** 383,400 ****
  		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2) * DEFAULT_SEL(operator);
  
  	/* Release stats. */
! 	if (mcv1_exists)
! 		free_attstatsslot(vardata1->atttype, mcv1_values, mcv1_nvalues,
! 						  mcv1_numbers, mcv1_nnumbers);
! 	if (mcv2_exists)
! 		free_attstatsslot(vardata2->atttype, mcv2_values, mcv2_nvalues,
! 						  mcv2_numbers, mcv2_nnumbers);
! 	if (hist1_exists)
! 		free_attstatsslot(vardata1->atttype, hist1_values, hist1_nvalues,
! 						  NULL, 0);
! 	if (hist2_exists)
! 		free_attstatsslot(vardata2->atttype, hist2_values, hist2_nvalues,
! 						  NULL, 0);
  
  	return selec;
  }
--- 371,380 ----
  		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2) * DEFAULT_SEL(operator);
  
  	/* Release stats. */
! 	free_attstatsslot(&mcv1_slot);
! 	free_attstatsslot(&mcv2_slot);
! 	free_attstatsslot(&hist1_slot);
! 	free_attstatsslot(&hist2_slot);
  
  	return selec;
  }
*************** networkjoinsel_semi(Oid operator,
*** 423,464 ****
  	int			opr_codenum;
  	FmgrInfo	proc;
  	int			i,
- 				mcv1_nvalues,
- 				mcv2_nvalues,
- 				mcv1_nnumbers,
- 				mcv2_nnumbers,
- 				hist1_nvalues,
- 				hist2_nvalues,
  				mcv1_length = 0,
  				mcv2_length = 0;
! 	Datum	   *mcv1_values,
! 			   *mcv2_values,
! 			   *hist1_values,
! 			   *hist2_values;
! 	float4	   *mcv1_numbers,
! 			   *mcv2_numbers;
  
  	if (HeapTupleIsValid(vardata1->statsTuple))
  	{
  		stats = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		nullfrac1 = stats->stanullfrac;
  
! 		mcv1_exists = get_attstatsslot(vardata1->statsTuple,
! 									   vardata1->atttype, vardata1->atttypmod,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 									   NULL,
! 									   &mcv1_values, &mcv1_nvalues,
! 									   &mcv1_numbers, &mcv1_nnumbers);
! 		hist1_exists = get_attstatsslot(vardata1->statsTuple,
! 									  vardata1->atttype, vardata1->atttypmod,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										NULL,
! 										&hist1_values, &hist1_nvalues,
! 										NULL, NULL);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv1_length = Min(mcv1_nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv1_exists)
! 			sumcommon1 = mcv_population(mcv1_numbers, mcv1_length);
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple))
--- 403,435 ----
  	int			opr_codenum;
  	FmgrInfo	proc;
  	int			i,
  				mcv1_length = 0,
  				mcv2_length = 0;
! 	AttStatsSlot mcv1_slot;
! 	AttStatsSlot mcv2_slot;
! 	AttStatsSlot hist1_slot;
! 	AttStatsSlot hist2_slot;
  
  	if (HeapTupleIsValid(vardata1->statsTuple))
  	{
  		stats = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		nullfrac1 = stats->stanullfrac;
  
! 		mcv1_exists = get_attstatsslot(&mcv1_slot, vardata1->statsTuple,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
! 		hist1_exists = get_attstatsslot(&hist1_slot, vardata1->statsTuple,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										ATTSTATSSLOT_VALUES);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv1_length = Min(mcv1_slot.nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv1_exists)
! 			sumcommon1 = mcv_population(mcv1_slot.numbers, mcv1_length);
! 	}
! 	else
! 	{
! 		memset(&mcv1_slot, 0, sizeof(mcv1_slot));
! 		memset(&hist1_slot, 0, sizeof(hist1_slot));
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple))
*************** networkjoinsel_semi(Oid operator,
*** 466,487 ****
  		stats = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
  		nullfrac2 = stats->stanullfrac;
  
! 		mcv2_exists = get_attstatsslot(vardata2->statsTuple,
! 									   vardata2->atttype, vardata2->atttypmod,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 									   NULL,
! 									   &mcv2_values, &mcv2_nvalues,
! 									   &mcv2_numbers, &mcv2_nnumbers);
! 		hist2_exists = get_attstatsslot(vardata2->statsTuple,
! 									  vardata2->atttype, vardata2->atttypmod,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										NULL,
! 										&hist2_values, &hist2_nvalues,
! 										NULL, NULL);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv2_length = Min(mcv2_nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv2_exists)
! 			sumcommon2 = mcv_population(mcv2_numbers, mcv2_length);
  	}
  
  	opr_codenum = inet_opr_codenum(operator);
--- 437,457 ----
  		stats = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
  		nullfrac2 = stats->stanullfrac;
  
! 		mcv2_exists = get_attstatsslot(&mcv2_slot, vardata2->statsTuple,
  									   STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
! 		hist2_exists = get_attstatsslot(&hist2_slot, vardata2->statsTuple,
  										STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 										ATTSTATSSLOT_VALUES);
  		/* Arbitrarily limit number of MCVs considered */
! 		mcv2_length = Min(mcv2_slot.nvalues, MAX_CONSIDERED_ELEMS);
  		if (mcv2_exists)
! 			sumcommon2 = mcv_population(mcv2_slot.numbers, mcv2_length);
! 	}
! 	else
! 	{
! 		memset(&mcv2_slot, 0, sizeof(mcv2_slot));
! 		memset(&hist2_slot, 0, sizeof(hist2_slot));
  	}
  
  	opr_codenum = inet_opr_codenum(operator);
*************** networkjoinsel_semi(Oid operator,
*** 499,508 ****
  	{
  		for (i = 0; i < mcv1_length; i++)
  		{
! 			selec += mcv1_numbers[i] *
! 				inet_semi_join_sel(mcv1_values[i],
! 								   mcv2_exists, mcv2_values, mcv2_length,
! 								   hist2_exists, hist2_values, hist2_nvalues,
  								   hist2_weight,
  								   &proc, opr_codenum);
  		}
--- 469,479 ----
  	{
  		for (i = 0; i < mcv1_length; i++)
  		{
! 			selec += mcv1_slot.numbers[i] *
! 				inet_semi_join_sel(mcv1_slot.values[i],
! 								   mcv2_exists, mcv2_slot.values, mcv2_length,
! 								   hist2_exists,
! 								   hist2_slot.values, hist2_slot.nvalues,
  								   hist2_weight,
  								   &proc, opr_codenum);
  		}
*************** networkjoinsel_semi(Oid operator,
*** 519,539 ****
  	 *
  	 * If there are too many histogram elements, decimate to limit runtime.
  	 */
! 	if (hist1_exists && hist1_nvalues > 2 && (mcv2_exists || hist2_exists))
  	{
  		double		hist_selec_sum = 0.0;
  		int			k,
  					n;
  
! 		k = (hist1_nvalues - 3) / MAX_CONSIDERED_ELEMS + 1;
  
  		n = 0;
! 		for (i = 1; i < hist1_nvalues - 1; i += k)
  		{
  			hist_selec_sum +=
! 				inet_semi_join_sel(hist1_values[i],
! 								   mcv2_exists, mcv2_values, mcv2_length,
! 								   hist2_exists, hist2_values, hist2_nvalues,
  								   hist2_weight,
  								   &proc, opr_codenum);
  			n++;
--- 490,511 ----
  	 *
  	 * If there are too many histogram elements, decimate to limit runtime.
  	 */
! 	if (hist1_exists && hist1_slot.nvalues > 2 && (mcv2_exists || hist2_exists))
  	{
  		double		hist_selec_sum = 0.0;
  		int			k,
  					n;
  
! 		k = (hist1_slot.nvalues - 3) / MAX_CONSIDERED_ELEMS + 1;
  
  		n = 0;
! 		for (i = 1; i < hist1_slot.nvalues - 1; i += k)
  		{
  			hist_selec_sum +=
! 				inet_semi_join_sel(hist1_slot.values[i],
! 								   mcv2_exists, mcv2_slot.values, mcv2_length,
! 								   hist2_exists,
! 								   hist2_slot.values, hist2_slot.nvalues,
  								   hist2_weight,
  								   &proc, opr_codenum);
  			n++;
*************** networkjoinsel_semi(Oid operator,
*** 550,567 ****
  		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2) * DEFAULT_SEL(operator);
  
  	/* Release stats. */
! 	if (mcv1_exists)
! 		free_attstatsslot(vardata1->atttype, mcv1_values, mcv1_nvalues,
! 						  mcv1_numbers, mcv1_nnumbers);
! 	if (mcv2_exists)
! 		free_attstatsslot(vardata2->atttype, mcv2_values, mcv2_nvalues,
! 						  mcv2_numbers, mcv2_nnumbers);
! 	if (hist1_exists)
! 		free_attstatsslot(vardata1->atttype, hist1_values, hist1_nvalues,
! 						  NULL, 0);
! 	if (hist2_exists)
! 		free_attstatsslot(vardata2->atttype, hist2_values, hist2_nvalues,
! 						  NULL, 0);
  
  	return selec;
  }
--- 522,531 ----
  		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2) * DEFAULT_SEL(operator);
  
  	/* Release stats. */
! 	free_attstatsslot(&mcv1_slot);
! 	free_attstatsslot(&mcv2_slot);
! 	free_attstatsslot(&hist1_slot);
! 	free_attstatsslot(&hist2_slot);
  
  	return selec;
  }
diff --git a/src/backend/utils/adt/rangetypes_selfuncs.c b/src/backend/utils/adt/rangetypes_selfuncs.c
index dbf1929..c4c5496 100644
*** a/src/backend/utils/adt/rangetypes_selfuncs.c
--- b/src/backend/utils/adt/rangetypes_selfuncs.c
*************** calc_rangesel(TypeCacheEntry *typcache, 
*** 239,263 ****
  	if (HeapTupleIsValid(vardata->statsTuple))
  	{
  		Form_pg_statistic stats;
! 		float4	   *numbers;
! 		int			nnumbers;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  		null_frac = stats->stanullfrac;
  
  		/* Try to get fraction of empty ranges */
! 		if (get_attstatsslot(vardata->statsTuple,
! 							 FLOAT8OID, -1,
  							 STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
  							 InvalidOid,
! 							 NULL,
! 							 NULL, NULL,
! 							 &numbers, &nnumbers))
  		{
! 			if (nnumbers != 1)
  				elog(ERROR, "invalid empty fraction statistic");		/* shouldn't happen */
! 			empty_frac = numbers[0];
! 			free_attstatsslot(FLOAT8OID, NULL, 0, numbers, nnumbers);
  		}
  		else
  		{
--- 239,259 ----
  	if (HeapTupleIsValid(vardata->statsTuple))
  	{
  		Form_pg_statistic stats;
! 		AttStatsSlot sslot;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  		null_frac = stats->stanullfrac;
  
  		/* Try to get fraction of empty ranges */
! 		if (get_attstatsslot(&sslot, vardata->statsTuple,
  							 STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
  							 InvalidOid,
! 							 ATTSTATSSLOT_NUMBERS))
  		{
! 			if (sslot.nnumbers != 1)
  				elog(ERROR, "invalid empty fraction statistic");		/* shouldn't happen */
! 			empty_frac = sslot.numbers[0];
! 			free_attstatsslot(&sslot);
  		}
  		else
  		{
*************** static double
*** 374,383 ****
  calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
  					  RangeType *constval, Oid operator)
  {
! 	Datum	   *hist_values;
  	int			nhist;
- 	Datum	   *length_hist_values = NULL;
- 	int			length_nhist = 0;
  	RangeBound *hist_lower;
  	RangeBound *hist_upper;
  	int			i;
--- 370,378 ----
  calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
  					  RangeType *constval, Oid operator)
  {
! 	AttStatsSlot hslot;
! 	AttStatsSlot lslot;
  	int			nhist;
  	RangeBound *hist_lower;
  	RangeBound *hist_upper;
  	int			i;
*************** calc_hist_selectivity(TypeCacheEntry *ty
*** 397,419 ****
  
  	/* Try to get histogram of ranges */
  	if (!(HeapTupleIsValid(vardata->statsTuple) &&
! 		  get_attstatsslot(vardata->statsTuple,
! 						   vardata->atttype, vardata->atttypmod,
  						   STATISTIC_KIND_BOUNDS_HISTOGRAM, InvalidOid,
! 						   NULL,
! 						   &hist_values, &nhist,
! 						   NULL, NULL)))
  		return -1.0;
  
  	/*
  	 * Convert histogram of ranges into histograms of its lower and upper
  	 * bounds.
  	 */
  	hist_lower = (RangeBound *) palloc(sizeof(RangeBound) * nhist);
  	hist_upper = (RangeBound *) palloc(sizeof(RangeBound) * nhist);
  	for (i = 0; i < nhist; i++)
  	{
! 		range_deserialize(typcache, DatumGetRangeType(hist_values[i]),
  						  &hist_lower[i], &hist_upper[i], &empty);
  		/* The histogram should not contain any empty ranges */
  		if (empty)
--- 392,412 ----
  
  	/* Try to get histogram of ranges */
  	if (!(HeapTupleIsValid(vardata->statsTuple) &&
! 		  get_attstatsslot(&hslot, vardata->statsTuple,
  						   STATISTIC_KIND_BOUNDS_HISTOGRAM, InvalidOid,
! 						   ATTSTATSSLOT_VALUES)))
  		return -1.0;
  
  	/*
  	 * Convert histogram of ranges into histograms of its lower and upper
  	 * bounds.
  	 */
+ 	nhist = hslot.nvalues;
  	hist_lower = (RangeBound *) palloc(sizeof(RangeBound) * nhist);
  	hist_upper = (RangeBound *) palloc(sizeof(RangeBound) * nhist);
  	for (i = 0; i < nhist; i++)
  	{
! 		range_deserialize(typcache, DatumGetRangeType(hslot.values[i]),
  						  &hist_lower[i], &hist_upper[i], &empty);
  		/* The histogram should not contain any empty ranges */
  		if (empty)
*************** calc_hist_selectivity(TypeCacheEntry *ty
*** 425,451 ****
  		operator == OID_RANGE_CONTAINED_OP)
  	{
  		if (!(HeapTupleIsValid(vardata->statsTuple) &&
! 			  get_attstatsslot(vardata->statsTuple,
! 							   FLOAT8OID, -1,
  							   STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
  							   InvalidOid,
! 							   NULL,
! 							   &length_hist_values, &length_nhist,
! 							   NULL, NULL)))
  		{
! 			free_attstatsslot(vardata->atttype, hist_values, nhist, NULL, 0);
  			return -1.0;
  		}
  
  		/* check that it's a histogram, not just a dummy entry */
! 		if (length_nhist < 2)
  		{
! 			free_attstatsslot(FLOAT8OID,
! 							  length_hist_values, length_nhist, NULL, 0);
! 			free_attstatsslot(vardata->atttype, hist_values, nhist, NULL, 0);
  			return -1.0;
  		}
  	}
  
  	/* Extract the bounds of the constant value. */
  	range_deserialize(typcache, constval, &const_lower, &const_upper, &empty);
--- 418,442 ----
  		operator == OID_RANGE_CONTAINED_OP)
  	{
  		if (!(HeapTupleIsValid(vardata->statsTuple) &&
! 			  get_attstatsslot(&lslot, vardata->statsTuple,
  							   STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
  							   InvalidOid,
! 							   ATTSTATSSLOT_VALUES)))
  		{
! 			free_attstatsslot(&hslot);
  			return -1.0;
  		}
  
  		/* check that it's a histogram, not just a dummy entry */
! 		if (lslot.nvalues < 2)
  		{
! 			free_attstatsslot(&lslot);
! 			free_attstatsslot(&hslot);
  			return -1.0;
  		}
  	}
+ 	else
+ 		memset(&lslot, 0, sizeof(lslot));
  
  	/* Extract the bounds of the constant value. */
  	range_deserialize(typcache, constval, &const_lower, &const_upper, &empty);
*************** calc_hist_selectivity(TypeCacheEntry *ty
*** 545,551 ****
  			hist_selec =
  				calc_hist_selectivity_contains(typcache, &const_lower,
  											 &const_upper, hist_lower, nhist,
! 										   length_hist_values, length_nhist);
  			break;
  
  		case OID_RANGE_CONTAINED_OP:
--- 536,542 ----
  			hist_selec =
  				calc_hist_selectivity_contains(typcache, &const_lower,
  											 &const_upper, hist_lower, nhist,
! 											   lslot.values, lslot.nvalues);
  			break;
  
  		case OID_RANGE_CONTAINED_OP:
*************** calc_hist_selectivity(TypeCacheEntry *ty
*** 570,576 ****
  				hist_selec =
  					calc_hist_selectivity_contained(typcache, &const_lower,
  											 &const_upper, hist_lower, nhist,
! 										   length_hist_values, length_nhist);
  			}
  			break;
  
--- 561,567 ----
  				hist_selec =
  					calc_hist_selectivity_contained(typcache, &const_lower,
  											 &const_upper, hist_lower, nhist,
! 												lslot.values, lslot.nvalues);
  			}
  			break;
  
*************** calc_hist_selectivity(TypeCacheEntry *ty
*** 580,588 ****
  			break;
  	}
  
! 	free_attstatsslot(FLOAT8OID,
! 					  length_hist_values, length_nhist, NULL, 0);
! 	free_attstatsslot(vardata->atttype, hist_values, nhist, NULL, 0);
  
  	return hist_selec;
  }
--- 571,578 ----
  			break;
  	}
  
! 	free_attstatsslot(&lslot);
! 	free_attstatsslot(&hslot);
  
  	return hist_selec;
  }
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 9b157f4..6c4cef9 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** var_eq_const(VariableStatData *vardata, 
*** 299,308 ****
  									  (opfuncoid = get_opcode(operator))))
  	{
  		Form_pg_statistic stats;
! 		Datum	   *values;
! 		int			nvalues;
! 		float4	   *numbers;
! 		int			nnumbers;
  		bool		match = false;
  		int			i;
  
--- 299,305 ----
  									  (opfuncoid = get_opcode(operator))))
  	{
  		Form_pg_statistic stats;
! 		AttStatsSlot sslot;
  		bool		match = false;
  		int			i;
  
*************** var_eq_const(VariableStatData *vardata, 
*** 315,344 ****
  		 * don't like this, maybe you shouldn't be using eqsel for your
  		 * operator...)
  		 */
! 		if (get_attstatsslot(vardata->statsTuple,
! 							 vardata->atttype, vardata->atttypmod,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 NULL,
! 							 &values, &nvalues,
! 							 &numbers, &nnumbers))
  		{
  			FmgrInfo	eqproc;
  
  			fmgr_info(opfuncoid, &eqproc);
  
! 			for (i = 0; i < nvalues; i++)
  			{
  				/* be careful to apply operator right way 'round */
  				if (varonleft)
  					match = DatumGetBool(FunctionCall2Coll(&eqproc,
  													   DEFAULT_COLLATION_OID,
! 														   values[i],
  														   constval));
  				else
  					match = DatumGetBool(FunctionCall2Coll(&eqproc,
  													   DEFAULT_COLLATION_OID,
  														   constval,
! 														   values[i]));
  				if (match)
  					break;
  			}
--- 312,338 ----
  		 * don't like this, maybe you shouldn't be using eqsel for your
  		 * operator...)
  		 */
! 		if (get_attstatsslot(&sslot, vardata->statsTuple,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  		{
  			FmgrInfo	eqproc;
  
  			fmgr_info(opfuncoid, &eqproc);
  
! 			for (i = 0; i < sslot.nvalues; i++)
  			{
  				/* be careful to apply operator right way 'round */
  				if (varonleft)
  					match = DatumGetBool(FunctionCall2Coll(&eqproc,
  													   DEFAULT_COLLATION_OID,
! 														   sslot.values[i],
  														   constval));
  				else
  					match = DatumGetBool(FunctionCall2Coll(&eqproc,
  													   DEFAULT_COLLATION_OID,
  														   constval,
! 														   sslot.values[i]));
  				if (match)
  					break;
  			}
*************** var_eq_const(VariableStatData *vardata, 
*** 346,354 ****
  		else
  		{
  			/* no most-common-value info available */
! 			values = NULL;
! 			numbers = NULL;
! 			i = nvalues = nnumbers = 0;
  		}
  
  		if (match)
--- 340,346 ----
  		else
  		{
  			/* no most-common-value info available */
! 			i = 0;				/* keep compiler quiet */
  		}
  
  		if (match)
*************** var_eq_const(VariableStatData *vardata, 
*** 357,363 ****
  			 * Constant is "=" to this common value.  We know selectivity
  			 * exactly (or as exactly as ANALYZE could calculate it, anyway).
  			 */
! 			selec = numbers[i];
  		}
  		else
  		{
--- 349,355 ----
  			 * Constant is "=" to this common value.  We know selectivity
  			 * exactly (or as exactly as ANALYZE could calculate it, anyway).
  			 */
! 			selec = sslot.numbers[i];
  		}
  		else
  		{
*************** var_eq_const(VariableStatData *vardata, 
*** 369,376 ****
  			double		sumcommon = 0.0;
  			double		otherdistinct;
  
! 			for (i = 0; i < nnumbers; i++)
! 				sumcommon += numbers[i];
  			selec = 1.0 - sumcommon - stats->stanullfrac;
  			CLAMP_PROBABILITY(selec);
  
--- 361,368 ----
  			double		sumcommon = 0.0;
  			double		otherdistinct;
  
! 			for (i = 0; i < sslot.nnumbers; i++)
! 				sumcommon += sslot.numbers[i];
  			selec = 1.0 - sumcommon - stats->stanullfrac;
  			CLAMP_PROBABILITY(selec);
  
*************** var_eq_const(VariableStatData *vardata, 
*** 379,385 ****
  			 * all the not-common values share this remaining fraction
  			 * equally, so we divide by the number of other distinct values.
  			 */
! 			otherdistinct = get_variable_numdistinct(vardata, &isdefault) - nnumbers;
  			if (otherdistinct > 1)
  				selec /= otherdistinct;
  
--- 371,378 ----
  			 * all the not-common values share this remaining fraction
  			 * equally, so we divide by the number of other distinct values.
  			 */
! 			otherdistinct = get_variable_numdistinct(vardata, &isdefault) -
! 				sslot.nnumbers;
  			if (otherdistinct > 1)
  				selec /= otherdistinct;
  
*************** var_eq_const(VariableStatData *vardata, 
*** 387,398 ****
  			 * Another cross-check: selectivity shouldn't be estimated as more
  			 * than the least common "most common value".
  			 */
! 			if (nnumbers > 0 && selec > numbers[nnumbers - 1])
! 				selec = numbers[nnumbers - 1];
  		}
  
! 		free_attstatsslot(vardata->atttype, values, nvalues,
! 						  numbers, nnumbers);
  	}
  	else
  	{
--- 380,390 ----
  			 * Another cross-check: selectivity shouldn't be estimated as more
  			 * than the least common "most common value".
  			 */
! 			if (sslot.nnumbers > 0 && selec > sslot.numbers[sslot.nnumbers - 1])
! 				selec = sslot.numbers[sslot.nnumbers - 1];
  		}
  
! 		free_attstatsslot(&sslot);
  	}
  	else
  	{
*************** var_eq_non_const(VariableStatData *varda
*** 435,442 ****
  	{
  		Form_pg_statistic stats;
  		double		ndistinct;
! 		float4	   *numbers;
! 		int			nnumbers;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  
--- 427,433 ----
  	{
  		Form_pg_statistic stats;
  		double		ndistinct;
! 		AttStatsSlot sslot;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
  
*************** var_eq_non_const(VariableStatData *varda
*** 459,474 ****
  		 * Cross-check: selectivity should never be estimated as more than the
  		 * most common value's.
  		 */
! 		if (get_attstatsslot(vardata->statsTuple,
! 							 vardata->atttype, vardata->atttypmod,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 NULL,
! 							 NULL, NULL,
! 							 &numbers, &nnumbers))
  		{
! 			if (nnumbers > 0 && selec > numbers[0])
! 				selec = numbers[0];
! 			free_attstatsslot(vardata->atttype, NULL, 0, numbers, nnumbers);
  		}
  	}
  	else
--- 450,462 ----
  		 * Cross-check: selectivity should never be estimated as more than the
  		 * most common value's.
  		 */
! 		if (get_attstatsslot(&sslot, vardata->statsTuple,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 ATTSTATSSLOT_NUMBERS))
  		{
! 			if (sslot.nnumbers > 0 && selec > sslot.numbers[0])
! 				selec = sslot.numbers[0];
! 			free_attstatsslot(&sslot);
  		}
  	}
  	else
*************** mcv_selectivity(VariableStatData *vardat
*** 620,629 ****
  {
  	double		mcv_selec,
  				sumcommon;
! 	Datum	   *values;
! 	int			nvalues;
! 	float4	   *numbers;
! 	int			nnumbers;
  	int			i;
  
  	mcv_selec = 0.0;
--- 608,614 ----
  {
  	double		mcv_selec,
  				sumcommon;
! 	AttStatsSlot sslot;
  	int			i;
  
  	mcv_selec = 0.0;
*************** mcv_selectivity(VariableStatData *vardat
*** 631,659 ****
  
  	if (HeapTupleIsValid(vardata->statsTuple) &&
  		statistic_proc_security_check(vardata, opproc->fn_oid) &&
! 		get_attstatsslot(vardata->statsTuple,
! 						 vardata->atttype, vardata->atttypmod,
  						 STATISTIC_KIND_MCV, InvalidOid,
! 						 NULL,
! 						 &values, &nvalues,
! 						 &numbers, &nnumbers))
  	{
! 		for (i = 0; i < nvalues; i++)
  		{
  			if (varonleft ?
  				DatumGetBool(FunctionCall2Coll(opproc,
  											   DEFAULT_COLLATION_OID,
! 											   values[i],
  											   constval)) :
  				DatumGetBool(FunctionCall2Coll(opproc,
  											   DEFAULT_COLLATION_OID,
  											   constval,
! 											   values[i])))
! 				mcv_selec += numbers[i];
! 			sumcommon += numbers[i];
  		}
! 		free_attstatsslot(vardata->atttype, values, nvalues,
! 						  numbers, nnumbers);
  	}
  
  	*sumcommonp = sumcommon;
--- 616,640 ----
  
  	if (HeapTupleIsValid(vardata->statsTuple) &&
  		statistic_proc_security_check(vardata, opproc->fn_oid) &&
! 		get_attstatsslot(&sslot, vardata->statsTuple,
  						 STATISTIC_KIND_MCV, InvalidOid,
! 						 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
  	{
! 		for (i = 0; i < sslot.nvalues; i++)
  		{
  			if (varonleft ?
  				DatumGetBool(FunctionCall2Coll(opproc,
  											   DEFAULT_COLLATION_OID,
! 											   sslot.values[i],
  											   constval)) :
  				DatumGetBool(FunctionCall2Coll(opproc,
  											   DEFAULT_COLLATION_OID,
  											   constval,
! 											   sslot.values[i])))
! 				mcv_selec += sslot.numbers[i];
! 			sumcommon += sslot.numbers[i];
  		}
! 		free_attstatsslot(&sslot);
  	}
  
  	*sumcommonp = sumcommon;
*************** histogram_selectivity(VariableStatData *
*** 699,706 ****
  					  int *hist_size)
  {
  	double		result;
! 	Datum	   *values;
! 	int			nvalues;
  
  	/* check sanity of parameters */
  	Assert(n_skip >= 0);
--- 680,686 ----
  					  int *hist_size)
  {
  	double		result;
! 	AttStatsSlot sslot;
  
  	/* check sanity of parameters */
  	Assert(n_skip >= 0);
*************** histogram_selectivity(VariableStatData *
*** 708,744 ****
  
  	if (HeapTupleIsValid(vardata->statsTuple) &&
  		statistic_proc_security_check(vardata, opproc->fn_oid) &&
! 		get_attstatsslot(vardata->statsTuple,
! 						 vardata->atttype, vardata->atttypmod,
  						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 						 NULL,
! 						 &values, &nvalues,
! 						 NULL, NULL))
  	{
! 		*hist_size = nvalues;
! 		if (nvalues >= min_hist_size)
  		{
  			int			nmatch = 0;
  			int			i;
  
! 			for (i = n_skip; i < nvalues - n_skip; i++)
  			{
  				if (varonleft ?
  					DatumGetBool(FunctionCall2Coll(opproc,
  												   DEFAULT_COLLATION_OID,
! 												   values[i],
  												   constval)) :
  					DatumGetBool(FunctionCall2Coll(opproc,
  												   DEFAULT_COLLATION_OID,
  												   constval,
! 												   values[i])))
  					nmatch++;
  			}
! 			result = ((double) nmatch) / ((double) (nvalues - 2 * n_skip));
  		}
  		else
  			result = -1;
! 		free_attstatsslot(vardata->atttype, values, nvalues, NULL, 0);
  	}
  	else
  	{
--- 688,721 ----
  
  	if (HeapTupleIsValid(vardata->statsTuple) &&
  		statistic_proc_security_check(vardata, opproc->fn_oid) &&
! 		get_attstatsslot(&sslot, vardata->statsTuple,
  						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 						 ATTSTATSSLOT_VALUES))
  	{
! 		*hist_size = sslot.nvalues;
! 		if (sslot.nvalues >= min_hist_size)
  		{
  			int			nmatch = 0;
  			int			i;
  
! 			for (i = n_skip; i < sslot.nvalues - n_skip; i++)
  			{
  				if (varonleft ?
  					DatumGetBool(FunctionCall2Coll(opproc,
  												   DEFAULT_COLLATION_OID,
! 												   sslot.values[i],
  												   constval)) :
  					DatumGetBool(FunctionCall2Coll(opproc,
  												   DEFAULT_COLLATION_OID,
  												   constval,
! 												   sslot.values[i])))
  					nmatch++;
  			}
! 			result = ((double) nmatch) / ((double) (sslot.nvalues - 2 * n_skip));
  		}
  		else
  			result = -1;
! 		free_attstatsslot(&sslot);
  	}
  	else
  	{
*************** ineq_histogram_selectivity(PlannerInfo *
*** 768,776 ****
  						   Datum constval, Oid consttype)
  {
  	double		hist_selec;
! 	Oid			hist_op;
! 	Datum	   *values;
! 	int			nvalues;
  
  	hist_selec = -1.0;
  
--- 745,751 ----
  						   Datum constval, Oid consttype)
  {
  	double		hist_selec;
! 	AttStatsSlot sslot;
  
  	hist_selec = -1.0;
  
*************** ineq_histogram_selectivity(PlannerInfo *
*** 786,799 ****
  	 */
  	if (HeapTupleIsValid(vardata->statsTuple) &&
  		statistic_proc_security_check(vardata, opproc->fn_oid) &&
! 		get_attstatsslot(vardata->statsTuple,
! 						 vardata->atttype, vardata->atttypmod,
  						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 						 &hist_op,
! 						 &values, &nvalues,
! 						 NULL, NULL))
  	{
! 		if (nvalues > 1)
  		{
  			/*
  			 * Use binary search to find proper location, ie, the first slot
--- 761,771 ----
  	 */
  	if (HeapTupleIsValid(vardata->statsTuple) &&
  		statistic_proc_security_check(vardata, opproc->fn_oid) &&
! 		get_attstatsslot(&sslot, vardata->statsTuple,
  						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 						 ATTSTATSSLOT_VALUES))
  	{
! 		if (sslot.nvalues > 1)
  		{
  			/*
  			 * Use binary search to find proper location, ie, the first slot
*************** ineq_histogram_selectivity(PlannerInfo *
*** 812,818 ****
  			 */
  			double		histfrac;
  			int			lobound = 0;	/* first possible slot to search */
! 			int			hibound = nvalues;		/* last+1 slot to search */
  			bool		have_end = false;
  
  			/*
--- 784,790 ----
  			 */
  			double		histfrac;
  			int			lobound = 0;	/* first possible slot to search */
! 			int			hibound = sslot.nvalues;		/* last+1 slot to search */
  			bool		have_end = false;
  
  			/*
*************** ineq_histogram_selectivity(PlannerInfo *
*** 821,832 ****
  			 * one of them to be updated, so we deal with that within the
  			 * loop.)
  			 */
! 			if (nvalues == 2)
  				have_end = get_actual_variable_range(root,
  													 vardata,
! 													 hist_op,
! 													 &values[0],
! 													 &values[1]);
  
  			while (lobound < hibound)
  			{
--- 793,804 ----
  			 * one of them to be updated, so we deal with that within the
  			 * loop.)
  			 */
! 			if (sslot.nvalues == 2)
  				have_end = get_actual_variable_range(root,
  													 vardata,
! 													 sslot.staop,
! 													 &sslot.values[0],
! 													 &sslot.values[1]);
  
  			while (lobound < hibound)
  			{
*************** ineq_histogram_selectivity(PlannerInfo *
*** 838,859 ****
  				 * histogram entry, first try to replace it with the actual
  				 * current min or max (unless we already did so above).
  				 */
! 				if (probe == 0 && nvalues > 2)
  					have_end = get_actual_variable_range(root,
  														 vardata,
! 														 hist_op,
! 														 &values[0],
  														 NULL);
! 				else if (probe == nvalues - 1 && nvalues > 2)
  					have_end = get_actual_variable_range(root,
  														 vardata,
! 														 hist_op,
  														 NULL,
! 														 &values[probe]);
  
  				ltcmp = DatumGetBool(FunctionCall2Coll(opproc,
  													   DEFAULT_COLLATION_OID,
! 													   values[probe],
  													   constval));
  				if (isgt)
  					ltcmp = !ltcmp;
--- 810,831 ----
  				 * histogram entry, first try to replace it with the actual
  				 * current min or max (unless we already did so above).
  				 */
! 				if (probe == 0 && sslot.nvalues > 2)
  					have_end = get_actual_variable_range(root,
  														 vardata,
! 														 sslot.staop,
! 														 &sslot.values[0],
  														 NULL);
! 				else if (probe == sslot.nvalues - 1 && sslot.nvalues > 2)
  					have_end = get_actual_variable_range(root,
  														 vardata,
! 														 sslot.staop,
  														 NULL,
! 													   &sslot.values[probe]);
  
  				ltcmp = DatumGetBool(FunctionCall2Coll(opproc,
  													   DEFAULT_COLLATION_OID,
! 													   sslot.values[probe],
  													   constval));
  				if (isgt)
  					ltcmp = !ltcmp;
*************** ineq_histogram_selectivity(PlannerInfo *
*** 868,874 ****
  				/* Constant is below lower histogram boundary. */
  				histfrac = 0.0;
  			}
! 			else if (lobound >= nvalues)
  			{
  				/* Constant is above upper histogram boundary. */
  				histfrac = 1.0;
--- 840,846 ----
  				/* Constant is below lower histogram boundary. */
  				histfrac = 0.0;
  			}
! 			else if (lobound >= sslot.nvalues)
  			{
  				/* Constant is above upper histogram boundary. */
  				histfrac = 1.0;
*************** ineq_histogram_selectivity(PlannerInfo *
*** 889,895 ****
  				 * interpolation within this bin.
  				 */
  				if (convert_to_scalar(constval, consttype, &val,
! 									  values[i - 1], values[i],
  									  vardata->vartype,
  									  &low, &high))
  				{
--- 861,867 ----
  				 * interpolation within this bin.
  				 */
  				if (convert_to_scalar(constval, consttype, &val,
! 									  sslot.values[i - 1], sslot.values[i],
  									  vardata->vartype,
  									  &low, &high))
  				{
*************** ineq_histogram_selectivity(PlannerInfo *
*** 936,942 ****
  				 * binfrac partial bin below the constant.
  				 */
  				histfrac = (double) (i - 1) + binfrac;
! 				histfrac /= (double) (nvalues - 1);
  			}
  
  			/*
--- 908,914 ----
  				 * binfrac partial bin below the constant.
  				 */
  				histfrac = (double) (i - 1) + binfrac;
! 				histfrac /= (double) (sslot.nvalues - 1);
  			}
  
  			/*
*************** ineq_histogram_selectivity(PlannerInfo *
*** 964,970 ****
  			}
  		}
  
! 		free_attstatsslot(vardata->atttype, values, nvalues, NULL, 0);
  	}
  
  	return hist_selec;
--- 936,942 ----
  			}
  		}
  
! 		free_attstatsslot(&sslot);
  	}
  
  	return hist_selec;
*************** booltestsel(PlannerInfo *root, BoolTestT
*** 1517,1537 ****
  	{
  		Form_pg_statistic stats;
  		double		freq_null;
! 		Datum	   *values;
! 		int			nvalues;
! 		float4	   *numbers;
! 		int			nnumbers;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
  		freq_null = stats->stanullfrac;
  
! 		if (get_attstatsslot(vardata.statsTuple,
! 							 vardata.atttype, vardata.atttypmod,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 NULL,
! 							 &values, &nvalues,
! 							 &numbers, &nnumbers)
! 			&& nnumbers > 0)
  		{
  			double		freq_true;
  			double		freq_false;
--- 1489,1503 ----
  	{
  		Form_pg_statistic stats;
  		double		freq_null;
! 		AttStatsSlot sslot;
  
  		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
  		freq_null = stats->stanullfrac;
  
! 		if (get_attstatsslot(&sslot, vardata.statsTuple,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS)
! 			&& sslot.nnumbers > 0)
  		{
  			double		freq_true;
  			double		freq_false;
*************** booltestsel(PlannerInfo *root, BoolTestT
*** 1539,1548 ****
  			/*
  			 * Get first MCV frequency and derive frequency for true.
  			 */
! 			if (DatumGetBool(values[0]))
! 				freq_true = numbers[0];
  			else
! 				freq_true = 1.0 - numbers[0] - freq_null;
  
  			/*
  			 * Next derive frequency for false. Then use these as appropriate
--- 1505,1514 ----
  			/*
  			 * Get first MCV frequency and derive frequency for true.
  			 */
! 			if (DatumGetBool(sslot.values[0]))
! 				freq_true = sslot.numbers[0];
  			else
! 				freq_true = 1.0 - sslot.numbers[0] - freq_null;
  
  			/*
  			 * Next derive frequency for false. Then use these as appropriate
*************** booltestsel(PlannerInfo *root, BoolTestT
*** 1583,1590 ****
  					break;
  			}
  
! 			free_attstatsslot(vardata.atttype, values, nvalues,
! 							  numbers, nnumbers);
  		}
  		else
  		{
--- 1549,1555 ----
  					break;
  			}
  
! 			free_attstatsslot(&sslot);
  		}
  		else
  		{
*************** eqjoinsel_inner(Oid operator,
*** 2274,2307 ****
  	Form_pg_statistic stats1 = NULL;
  	Form_pg_statistic stats2 = NULL;
  	bool		have_mcvs1 = false;
- 	Datum	   *values1 = NULL;
- 	int			nvalues1 = 0;
- 	float4	   *numbers1 = NULL;
- 	int			nnumbers1 = 0;
  	bool		have_mcvs2 = false;
! 	Datum	   *values2 = NULL;
! 	int			nvalues2 = 0;
! 	float4	   *numbers2 = NULL;
! 	int			nnumbers2 = 0;
  
  	nd1 = get_variable_numdistinct(vardata1, &isdefault1);
  	nd2 = get_variable_numdistinct(vardata2, &isdefault2);
  
  	opfuncoid = get_opcode(operator);
  
  	if (HeapTupleIsValid(vardata1->statsTuple))
  	{
  		/* note we allow use of nullfrac regardless of security check */
  		stats1 = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		if (statistic_proc_security_check(vardata1, opfuncoid))
! 			have_mcvs1 = get_attstatsslot(vardata1->statsTuple,
! 										  vardata1->atttype,
! 										  vardata1->atttypmod,
! 										  STATISTIC_KIND_MCV,
! 										  InvalidOid,
! 										  NULL,
! 										  &values1, &nvalues1,
! 										  &numbers1, &nnumbers1);
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple))
--- 2239,2264 ----
  	Form_pg_statistic stats1 = NULL;
  	Form_pg_statistic stats2 = NULL;
  	bool		have_mcvs1 = false;
  	bool		have_mcvs2 = false;
! 	AttStatsSlot sslot1;
! 	AttStatsSlot sslot2;
  
  	nd1 = get_variable_numdistinct(vardata1, &isdefault1);
  	nd2 = get_variable_numdistinct(vardata2, &isdefault2);
  
  	opfuncoid = get_opcode(operator);
  
+ 	memset(&sslot1, 0, sizeof(sslot1));
+ 	memset(&sslot2, 0, sizeof(sslot2));
+ 
  	if (HeapTupleIsValid(vardata1->statsTuple))
  	{
  		/* note we allow use of nullfrac regardless of security check */
  		stats1 = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		if (statistic_proc_security_check(vardata1, opfuncoid))
! 			have_mcvs1 = get_attstatsslot(&sslot1, vardata1->statsTuple,
! 										  STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple))
*************** eqjoinsel_inner(Oid operator,
*** 2309,2322 ****
  		/* note we allow use of nullfrac regardless of security check */
  		stats2 = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
  		if (statistic_proc_security_check(vardata2, opfuncoid))
! 			have_mcvs2 = get_attstatsslot(vardata2->statsTuple,
! 										  vardata2->atttype,
! 										  vardata2->atttypmod,
! 										  STATISTIC_KIND_MCV,
! 										  InvalidOid,
! 										  NULL,
! 										  &values2, &nvalues2,
! 										  &numbers2, &nnumbers2);
  	}
  
  	if (have_mcvs1 && have_mcvs2)
--- 2266,2274 ----
  		/* note we allow use of nullfrac regardless of security check */
  		stats2 = (Form_pg_statistic) GETSTRUCT(vardata2->statsTuple);
  		if (statistic_proc_security_check(vardata2, opfuncoid))
! 			have_mcvs2 = get_attstatsslot(&sslot2, vardata2->statsTuple,
! 										  STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
  	}
  
  	if (have_mcvs1 && have_mcvs2)
*************** eqjoinsel_inner(Oid operator,
*** 2351,2358 ****
  					nmatches;
  
  		fmgr_info(opfuncoid, &eqproc);
! 		hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
! 		hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
  
  		/*
  		 * Note we assume that each MCV will match at most one member of the
--- 2303,2310 ----
  					nmatches;
  
  		fmgr_info(opfuncoid, &eqproc);
! 		hasmatch1 = (bool *) palloc0(sslot1.nvalues * sizeof(bool));
! 		hasmatch2 = (bool *) palloc0(sslot2.nvalues * sizeof(bool));
  
  		/*
  		 * Note we assume that each MCV will match at most one member of the
*************** eqjoinsel_inner(Oid operator,
*** 2362,2382 ****
  		 */
  		matchprodfreq = 0.0;
  		nmatches = 0;
! 		for (i = 0; i < nvalues1; i++)
  		{
  			int			j;
  
! 			for (j = 0; j < nvalues2; j++)
  			{
  				if (hasmatch2[j])
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
  												   DEFAULT_COLLATION_OID,
! 												   values1[i],
! 												   values2[j])))
  				{
  					hasmatch1[i] = hasmatch2[j] = true;
! 					matchprodfreq += numbers1[i] * numbers2[j];
  					nmatches++;
  					break;
  				}
--- 2314,2334 ----
  		 */
  		matchprodfreq = 0.0;
  		nmatches = 0;
! 		for (i = 0; i < sslot1.nvalues; i++)
  		{
  			int			j;
  
! 			for (j = 0; j < sslot2.nvalues; j++)
  			{
  				if (hasmatch2[j])
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
  												   DEFAULT_COLLATION_OID,
! 												   sslot1.values[i],
! 												   sslot2.values[j])))
  				{
  					hasmatch1[i] = hasmatch2[j] = true;
! 					matchprodfreq += sslot1.numbers[i] * sslot2.numbers[j];
  					nmatches++;
  					break;
  				}
*************** eqjoinsel_inner(Oid operator,
*** 2385,2406 ****
  		CLAMP_PROBABILITY(matchprodfreq);
  		/* Sum up frequencies of matched and unmatched MCVs */
  		matchfreq1 = unmatchfreq1 = 0.0;
! 		for (i = 0; i < nvalues1; i++)
  		{
  			if (hasmatch1[i])
! 				matchfreq1 += numbers1[i];
  			else
! 				unmatchfreq1 += numbers1[i];
  		}
  		CLAMP_PROBABILITY(matchfreq1);
  		CLAMP_PROBABILITY(unmatchfreq1);
  		matchfreq2 = unmatchfreq2 = 0.0;
! 		for (i = 0; i < nvalues2; i++)
  		{
  			if (hasmatch2[i])
! 				matchfreq2 += numbers2[i];
  			else
! 				unmatchfreq2 += numbers2[i];
  		}
  		CLAMP_PROBABILITY(matchfreq2);
  		CLAMP_PROBABILITY(unmatchfreq2);
--- 2337,2358 ----
  		CLAMP_PROBABILITY(matchprodfreq);
  		/* Sum up frequencies of matched and unmatched MCVs */
  		matchfreq1 = unmatchfreq1 = 0.0;
! 		for (i = 0; i < sslot1.nvalues; i++)
  		{
  			if (hasmatch1[i])
! 				matchfreq1 += sslot1.numbers[i];
  			else
! 				unmatchfreq1 += sslot1.numbers[i];
  		}
  		CLAMP_PROBABILITY(matchfreq1);
  		CLAMP_PROBABILITY(unmatchfreq1);
  		matchfreq2 = unmatchfreq2 = 0.0;
! 		for (i = 0; i < sslot2.nvalues; i++)
  		{
  			if (hasmatch2[i])
! 				matchfreq2 += sslot2.numbers[i];
  			else
! 				unmatchfreq2 += sslot2.numbers[i];
  		}
  		CLAMP_PROBABILITY(matchfreq2);
  		CLAMP_PROBABILITY(unmatchfreq2);
*************** eqjoinsel_inner(Oid operator,
*** 2425,2439 ****
  		 * MCVs plus non-MCV values.
  		 */
  		totalsel1 = matchprodfreq;
! 		if (nd2 > nvalues2)
! 			totalsel1 += unmatchfreq1 * otherfreq2 / (nd2 - nvalues2);
  		if (nd2 > nmatches)
  			totalsel1 += otherfreq1 * (otherfreq2 + unmatchfreq2) /
  				(nd2 - nmatches);
  		/* Same estimate from the point of view of relation 2. */
  		totalsel2 = matchprodfreq;
! 		if (nd1 > nvalues1)
! 			totalsel2 += unmatchfreq2 * otherfreq1 / (nd1 - nvalues1);
  		if (nd1 > nmatches)
  			totalsel2 += otherfreq2 * (otherfreq1 + unmatchfreq1) /
  				(nd1 - nmatches);
--- 2377,2391 ----
  		 * MCVs plus non-MCV values.
  		 */
  		totalsel1 = matchprodfreq;
! 		if (nd2 > sslot2.nvalues)
! 			totalsel1 += unmatchfreq1 * otherfreq2 / (nd2 - sslot2.nvalues);
  		if (nd2 > nmatches)
  			totalsel1 += otherfreq1 * (otherfreq2 + unmatchfreq2) /
  				(nd2 - nmatches);
  		/* Same estimate from the point of view of relation 2. */
  		totalsel2 = matchprodfreq;
! 		if (nd1 > sslot1.nvalues)
! 			totalsel2 += unmatchfreq2 * otherfreq1 / (nd1 - sslot1.nvalues);
  		if (nd1 > nmatches)
  			totalsel2 += otherfreq2 * (otherfreq1 + unmatchfreq1) /
  				(nd1 - nmatches);
*************** eqjoinsel_inner(Oid operator,
*** 2478,2489 ****
  			selec /= nd2;
  	}
  
! 	if (have_mcvs1)
! 		free_attstatsslot(vardata1->atttype, values1, nvalues1,
! 						  numbers1, nnumbers1);
! 	if (have_mcvs2)
! 		free_attstatsslot(vardata2->atttype, values2, nvalues2,
! 						  numbers2, nnumbers2);
  
  	return selec;
  }
--- 2430,2437 ----
  			selec /= nd2;
  	}
  
! 	free_attstatsslot(&sslot1);
! 	free_attstatsslot(&sslot2);
  
  	return selec;
  }
*************** eqjoinsel_semi(Oid operator,
*** 2508,2528 ****
  	Oid			opfuncoid;
  	Form_pg_statistic stats1 = NULL;
  	bool		have_mcvs1 = false;
- 	Datum	   *values1 = NULL;
- 	int			nvalues1 = 0;
- 	float4	   *numbers1 = NULL;
- 	int			nnumbers1 = 0;
  	bool		have_mcvs2 = false;
! 	Datum	   *values2 = NULL;
! 	int			nvalues2 = 0;
! 	float4	   *numbers2 = NULL;
! 	int			nnumbers2 = 0;
  
  	nd1 = get_variable_numdistinct(vardata1, &isdefault1);
  	nd2 = get_variable_numdistinct(vardata2, &isdefault2);
  
  	opfuncoid = OidIsValid(operator) ? get_opcode(operator) : InvalidOid;
  
  	/*
  	 * We clamp nd2 to be not more than what we estimate the inner relation's
  	 * size to be.  This is intuitively somewhat reasonable since obviously
--- 2456,2473 ----
  	Oid			opfuncoid;
  	Form_pg_statistic stats1 = NULL;
  	bool		have_mcvs1 = false;
  	bool		have_mcvs2 = false;
! 	AttStatsSlot sslot1;
! 	AttStatsSlot sslot2;
  
  	nd1 = get_variable_numdistinct(vardata1, &isdefault1);
  	nd2 = get_variable_numdistinct(vardata2, &isdefault2);
  
  	opfuncoid = OidIsValid(operator) ? get_opcode(operator) : InvalidOid;
  
+ 	memset(&sslot1, 0, sizeof(sslot1));
+ 	memset(&sslot2, 0, sizeof(sslot2));
+ 
  	/*
  	 * We clamp nd2 to be not more than what we estimate the inner relation's
  	 * size to be.  This is intuitively somewhat reasonable since obviously
*************** eqjoinsel_semi(Oid operator,
*** 2561,2587 ****
  		/* note we allow use of nullfrac regardless of security check */
  		stats1 = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		if (statistic_proc_security_check(vardata1, opfuncoid))
! 			have_mcvs1 = get_attstatsslot(vardata1->statsTuple,
! 										  vardata1->atttype,
! 										  vardata1->atttypmod,
! 										  STATISTIC_KIND_MCV,
! 										  InvalidOid,
! 										  NULL,
! 										  &values1, &nvalues1,
! 										  &numbers1, &nnumbers1);
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple) &&
  		statistic_proc_security_check(vardata2, opfuncoid))
  	{
! 		have_mcvs2 = get_attstatsslot(vardata2->statsTuple,
! 									  vardata2->atttype,
! 									  vardata2->atttypmod,
! 									  STATISTIC_KIND_MCV,
! 									  InvalidOid,
! 									  NULL,
! 									  &values2, &nvalues2,
! 									  &numbers2, &nnumbers2);
  	}
  
  	if (have_mcvs1 && have_mcvs2 && OidIsValid(operator))
--- 2506,2523 ----
  		/* note we allow use of nullfrac regardless of security check */
  		stats1 = (Form_pg_statistic) GETSTRUCT(vardata1->statsTuple);
  		if (statistic_proc_security_check(vardata1, opfuncoid))
! 			have_mcvs1 = get_attstatsslot(&sslot1, vardata1->statsTuple,
! 										  STATISTIC_KIND_MCV, InvalidOid,
! 								 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
  	}
  
  	if (HeapTupleIsValid(vardata2->statsTuple) &&
  		statistic_proc_security_check(vardata2, opfuncoid))
  	{
! 		have_mcvs2 = get_attstatsslot(&sslot2, vardata2->statsTuple,
! 									  STATISTIC_KIND_MCV, InvalidOid,
! 									  ATTSTATSSLOT_VALUES);
! 		/* note: currently don't need stanumbers from RHS */
  	}
  
  	if (have_mcvs1 && have_mcvs2 && OidIsValid(operator))
*************** eqjoinsel_semi(Oid operator,
*** 2607,2621 ****
  
  		/*
  		 * The clamping above could have resulted in nd2 being less than
! 		 * nvalues2; in which case, we assume that precisely the nd2 most
! 		 * common values in the relation will appear in the join input, and so
! 		 * compare to only the first nd2 members of the MCV list.  Of course
! 		 * this is frequently wrong, but it's the best bet we can make.
  		 */
! 		clamped_nvalues2 = Min(nvalues2, nd2);
  
  		fmgr_info(opfuncoid, &eqproc);
! 		hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
  		hasmatch2 = (bool *) palloc0(clamped_nvalues2 * sizeof(bool));
  
  		/*
--- 2543,2557 ----
  
  		/*
  		 * The clamping above could have resulted in nd2 being less than
! 		 * sslot2.nvalues; in which case, we assume that precisely the nd2
! 		 * most common values in the relation will appear in the join input,
! 		 * and so compare to only the first nd2 members of the MCV list.  Of
! 		 * course this is frequently wrong, but it's the best bet we can make.
  		 */
! 		clamped_nvalues2 = Min(sslot2.nvalues, nd2);
  
  		fmgr_info(opfuncoid, &eqproc);
! 		hasmatch1 = (bool *) palloc0(sslot1.nvalues * sizeof(bool));
  		hasmatch2 = (bool *) palloc0(clamped_nvalues2 * sizeof(bool));
  
  		/*
*************** eqjoinsel_semi(Oid operator,
*** 2625,2631 ****
  		 * and because the math wouldn't add up...
  		 */
  		nmatches = 0;
! 		for (i = 0; i < nvalues1; i++)
  		{
  			int			j;
  
--- 2561,2567 ----
  		 * and because the math wouldn't add up...
  		 */
  		nmatches = 0;
! 		for (i = 0; i < sslot1.nvalues; i++)
  		{
  			int			j;
  
*************** eqjoinsel_semi(Oid operator,
*** 2635,2642 ****
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
  												   DEFAULT_COLLATION_OID,
! 												   values1[i],
! 												   values2[j])))
  				{
  					hasmatch1[i] = hasmatch2[j] = true;
  					nmatches++;
--- 2571,2578 ----
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
  												   DEFAULT_COLLATION_OID,
! 												   sslot1.values[i],
! 												   sslot2.values[j])))
  				{
  					hasmatch1[i] = hasmatch2[j] = true;
  					nmatches++;
*************** eqjoinsel_semi(Oid operator,
*** 2646,2655 ****
  		}
  		/* Sum up frequencies of matched MCVs */
  		matchfreq1 = 0.0;
! 		for (i = 0; i < nvalues1; i++)
  		{
  			if (hasmatch1[i])
! 				matchfreq1 += numbers1[i];
  		}
  		CLAMP_PROBABILITY(matchfreq1);
  		pfree(hasmatch1);
--- 2582,2591 ----
  		}
  		/* Sum up frequencies of matched MCVs */
  		matchfreq1 = 0.0;
! 		for (i = 0; i < sslot1.nvalues; i++)
  		{
  			if (hasmatch1[i])
! 				matchfreq1 += sslot1.numbers[i];
  		}
  		CLAMP_PROBABILITY(matchfreq1);
  		pfree(hasmatch1);
*************** eqjoinsel_semi(Oid operator,
*** 2704,2715 ****
  			selec = 0.5 * (1.0 - nullfrac1);
  	}
  
! 	if (have_mcvs1)
! 		free_attstatsslot(vardata1->atttype, values1, nvalues1,
! 						  numbers1, nnumbers1);
! 	if (have_mcvs2)
! 		free_attstatsslot(vardata2->atttype, values2, nvalues2,
! 						  numbers2, nnumbers2);
  
  	return selec;
  }
--- 2640,2647 ----
  			selec = 0.5 * (1.0 - nullfrac1);
  	}
  
! 	free_attstatsslot(&sslot1);
! 	free_attstatsslot(&sslot2);
  
  	return selec;
  }
*************** estimate_hash_bucketsize(PlannerInfo *ro
*** 3629,3636 ****
  				mcvfreq,
  				avgfreq;
  	bool		isdefault;
! 	float4	   *numbers;
! 	int			nnumbers;
  
  	examine_variable(root, hashkey, 0, &vardata);
  
--- 3561,3567 ----
  				mcvfreq,
  				avgfreq;
  	bool		isdefault;
! 	AttStatsSlot sslot;
  
  	examine_variable(root, hashkey, 0, &vardata);
  
*************** estimate_hash_bucketsize(PlannerInfo *ro
*** 3689,3708 ****
  
  	if (HeapTupleIsValid(vardata.statsTuple))
  	{
! 		if (get_attstatsslot(vardata.statsTuple,
! 							 vardata.atttype, vardata.atttypmod,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 NULL,
! 							 NULL, NULL,
! 							 &numbers, &nnumbers))
  		{
  			/*
  			 * The first MCV stat is for the most common value.
  			 */
! 			if (nnumbers > 0)
! 				mcvfreq = numbers[0];
! 			free_attstatsslot(vardata.atttype, NULL, 0,
! 							  numbers, nnumbers);
  		}
  	}
  
--- 3620,3635 ----
  
  	if (HeapTupleIsValid(vardata.statsTuple))
  	{
! 		if (get_attstatsslot(&sslot, vardata.statsTuple,
  							 STATISTIC_KIND_MCV, InvalidOid,
! 							 ATTSTATSSLOT_NUMBERS))
  		{
  			/*
  			 * The first MCV stat is for the most common value.
  			 */
! 			if (sslot.nnumbers > 0)
! 				mcvfreq = sslot.numbers[0];
! 			free_attstatsslot(&sslot);
  		}
  	}
  
*************** get_join_variables(PlannerInfo *root, Li
*** 4572,4578 ****
   *	freefunc: pointer to a function to release statsTuple with.
   *	vartype: exposed type of the expression; this should always match
   *		the declared input type of the operator we are estimating for.
!  *	atttype, atttypmod: type data to pass to get_attstatsslot().  This is
   *		commonly the same as the exposed type of the variable argument,
   *		but can be different in binary-compatible-type cases.
   *	isunique: TRUE if we were able to match the var to a unique index or a
--- 4499,4505 ----
   *	freefunc: pointer to a function to release statsTuple with.
   *	vartype: exposed type of the expression; this should always match
   *		the declared input type of the operator we are estimating for.
!  *	atttype, atttypmod: actual type/typmod of the "var" expression.  This is
   *		commonly the same as the exposed type of the variable argument,
   *		but can be different in binary-compatible-type cases.
   *	isunique: TRUE if we were able to match the var to a unique index or a
*************** get_variable_range(PlannerInfo *root, Va
*** 5125,5132 ****
  	int16		typLen;
  	bool		typByVal;
  	Oid			opfuncoid;
! 	Datum	   *values;
! 	int			nvalues;
  	int			i;
  
  	/*
--- 5052,5058 ----
  	int16		typLen;
  	bool		typByVal;
  	Oid			opfuncoid;
! 	AttStatsSlot sslot;
  	int			i;
  
  	/*
*************** get_variable_range(PlannerInfo *root, Va
*** 5167,5195 ****
  	 * the one we want, fail --- this suggests that there is data we can't
  	 * use.
  	 */
! 	if (get_attstatsslot(vardata->statsTuple,
! 						 vardata->atttype, vardata->atttypmod,
  						 STATISTIC_KIND_HISTOGRAM, sortop,
! 						 NULL,
! 						 &values, &nvalues,
! 						 NULL, NULL))
  	{
! 		if (nvalues > 0)
  		{
! 			tmin = datumCopy(values[0], typByVal, typLen);
! 			tmax = datumCopy(values[nvalues - 1], typByVal, typLen);
  			have_data = true;
  		}
! 		free_attstatsslot(vardata->atttype, values, nvalues, NULL, 0);
  	}
! 	else if (get_attstatsslot(vardata->statsTuple,
! 							  vardata->atttype, vardata->atttypmod,
  							  STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 							  NULL,
! 							  &values, &nvalues,
! 							  NULL, NULL))
  	{
! 		free_attstatsslot(vardata->atttype, values, nvalues, NULL, 0);
  		return false;
  	}
  
--- 5093,5115 ----
  	 * the one we want, fail --- this suggests that there is data we can't
  	 * use.
  	 */
! 	if (get_attstatsslot(&sslot, vardata->statsTuple,
  						 STATISTIC_KIND_HISTOGRAM, sortop,
! 						 ATTSTATSSLOT_VALUES))
  	{
! 		if (sslot.nvalues > 0)
  		{
! 			tmin = datumCopy(sslot.values[0], typByVal, typLen);
! 			tmax = datumCopy(sslot.values[sslot.nvalues - 1], typByVal, typLen);
  			have_data = true;
  		}
! 		free_attstatsslot(&sslot);
  	}
! 	else if (get_attstatsslot(&sslot, vardata->statsTuple,
  							  STATISTIC_KIND_HISTOGRAM, InvalidOid,
! 							  0))
  	{
! 		free_attstatsslot(&sslot);
  		return false;
  	}
  
*************** get_variable_range(PlannerInfo *root, Va
*** 5199,5210 ****
  	 * the MCVs.  However, usually the MCVs will not be the extreme values, so
  	 * avoid unnecessary data copying.
  	 */
! 	if (get_attstatsslot(vardata->statsTuple,
! 						 vardata->atttype, vardata->atttypmod,
  						 STATISTIC_KIND_MCV, InvalidOid,
! 						 NULL,
! 						 &values, &nvalues,
! 						 NULL, NULL))
  	{
  		bool		tmin_is_mcv = false;
  		bool		tmax_is_mcv = false;
--- 5119,5127 ----
  	 * the MCVs.  However, usually the MCVs will not be the extreme values, so
  	 * avoid unnecessary data copying.
  	 */
! 	if (get_attstatsslot(&sslot, vardata->statsTuple,
  						 STATISTIC_KIND_MCV, InvalidOid,
! 						 ATTSTATSSLOT_VALUES))
  	{
  		bool		tmin_is_mcv = false;
  		bool		tmax_is_mcv = false;
*************** get_variable_range(PlannerInfo *root, Va
*** 5212,5237 ****
  
  		fmgr_info(opfuncoid, &opproc);
  
! 		for (i = 0; i < nvalues; i++)
  		{
  			if (!have_data)
  			{
! 				tmin = tmax = values[i];
  				tmin_is_mcv = tmax_is_mcv = have_data = true;
  				continue;
  			}
  			if (DatumGetBool(FunctionCall2Coll(&opproc,
  											   DEFAULT_COLLATION_OID,
! 											   values[i], tmin)))
  			{
! 				tmin = values[i];
  				tmin_is_mcv = true;
  			}
  			if (DatumGetBool(FunctionCall2Coll(&opproc,
  											   DEFAULT_COLLATION_OID,
! 											   tmax, values[i])))
  			{
! 				tmax = values[i];
  				tmax_is_mcv = true;
  			}
  		}
--- 5129,5154 ----
  
  		fmgr_info(opfuncoid, &opproc);
  
! 		for (i = 0; i < sslot.nvalues; i++)
  		{
  			if (!have_data)
  			{
! 				tmin = tmax = sslot.values[i];
  				tmin_is_mcv = tmax_is_mcv = have_data = true;
  				continue;
  			}
  			if (DatumGetBool(FunctionCall2Coll(&opproc,
  											   DEFAULT_COLLATION_OID,
! 											   sslot.values[i], tmin)))
  			{
! 				tmin = sslot.values[i];
  				tmin_is_mcv = true;
  			}
  			if (DatumGetBool(FunctionCall2Coll(&opproc,
  											   DEFAULT_COLLATION_OID,
! 											   tmax, sslot.values[i])))
  			{
! 				tmax = sslot.values[i];
  				tmax_is_mcv = true;
  			}
  		}
*************** get_variable_range(PlannerInfo *root, Va
*** 5239,5245 ****
  			tmin = datumCopy(tmin, typByVal, typLen);
  		if (tmax_is_mcv)
  			tmax = datumCopy(tmax, typByVal, typLen);
! 		free_attstatsslot(vardata->atttype, values, nvalues, NULL, 0);
  	}
  
  	*min = tmin;
--- 5156,5162 ----
  			tmin = datumCopy(tmin, typByVal, typLen);
  		if (tmax_is_mcv)
  			tmax = datumCopy(tmax, typByVal, typLen);
! 		free_attstatsslot(&sslot);
  	}
  
  	*min = tmin;
*************** btcostestimate(PlannerInfo *root, IndexP
*** 6979,7003 ****
  	if (HeapTupleIsValid(vardata.statsTuple))
  	{
  		Oid			sortop;
! 		float4	   *numbers;
! 		int			nnumbers;
  
  		sortop = get_opfamily_member(index->opfamily[0],
  									 index->opcintype[0],
  									 index->opcintype[0],
  									 BTLessStrategyNumber);
  		if (OidIsValid(sortop) &&
! 			get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
! 							 STATISTIC_KIND_CORRELATION,
! 							 sortop,
! 							 NULL,
! 							 NULL, NULL,
! 							 &numbers, &nnumbers))
  		{
  			double		varCorrelation;
  
! 			Assert(nnumbers == 1);
! 			varCorrelation = numbers[0];
  
  			if (index->reverse_sort[0])
  				varCorrelation = -varCorrelation;
--- 6896,6916 ----
  	if (HeapTupleIsValid(vardata.statsTuple))
  	{
  		Oid			sortop;
! 		AttStatsSlot sslot;
  
  		sortop = get_opfamily_member(index->opfamily[0],
  									 index->opcintype[0],
  									 index->opcintype[0],
  									 BTLessStrategyNumber);
  		if (OidIsValid(sortop) &&
! 			get_attstatsslot(&sslot, vardata.statsTuple,
! 							 STATISTIC_KIND_CORRELATION, sortop,
! 							 ATTSTATSSLOT_NUMBERS))
  		{
  			double		varCorrelation;
  
! 			Assert(sslot.nnumbers == 1);
! 			varCorrelation = sslot.numbers[0];
  
  			if (index->reverse_sort[0])
  				varCorrelation = -varCorrelation;
*************** btcostestimate(PlannerInfo *root, IndexP
*** 7007,7013 ****
  			else
  				costs.indexCorrelation = varCorrelation;
  
! 			free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
  		}
  	}
  
--- 6920,6926 ----
  			else
  				costs.indexCorrelation = varCorrelation;
  
! 			free_attstatsslot(&sslot);
  		}
  	}
  
*************** brincostestimate(PlannerInfo *root, Inde
*** 7920,7944 ****
  
  		if (HeapTupleIsValid(vardata.statsTuple))
  		{
! 			float4	   *numbers;
! 			int			nnumbers;
  
! 			if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
! 								 STATISTIC_KIND_CORRELATION,
! 								 InvalidOid,
! 								 NULL,
! 								 NULL, NULL,
! 								 &numbers, &nnumbers))
  			{
  				double		varCorrelation = 0.0;
  
! 				if (nnumbers > 0)
! 					varCorrelation = Abs(numbers[0]);
  
  				if (varCorrelation > *indexCorrelation)
  					*indexCorrelation = varCorrelation;
  
! 				free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
  			}
  		}
  
--- 7833,7853 ----
  
  		if (HeapTupleIsValid(vardata.statsTuple))
  		{
! 			AttStatsSlot sslot;
  
! 			if (get_attstatsslot(&sslot, vardata.statsTuple,
! 								 STATISTIC_KIND_CORRELATION, InvalidOid,
! 								 ATTSTATSSLOT_NUMBERS))
  			{
  				double		varCorrelation = 0.0;
  
! 				if (sslot.nnumbers > 0)
! 					varCorrelation = Abs(sslot.numbers[0]);
  
  				if (varCorrelation > *indexCorrelation)
  					*indexCorrelation = varCorrelation;
  
! 				free_attstatsslot(&sslot);
  			}
  		}
  
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 236d876..b94d475 100644
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
*************** get_attavgwidth(Oid relid, AttrNumber at
*** 2864,2898 ****
   * that have been provided by a stats hook and didn't really come from
   * pg_statistic.
   *
   * statstuple: pg_statistic tuple to be examined.
-  * atttype: type OID of slot's stavalues (can be InvalidOid if values == NULL).
-  * atttypmod: typmod of slot's stavalues (can be 0 if values == NULL).
   * reqkind: STAKIND code for desired statistics slot kind.
   * reqop: STAOP value wanted, or InvalidOid if don't care.
!  * actualop: if not NULL, *actualop receives the actual STAOP value.
!  * values, nvalues: if not NULL, the slot's stavalues are extracted.
!  * numbers, nnumbers: if not NULL, the slot's stanumbers are extracted.
   *
!  * If assigned, values and numbers are set to point to palloc'd arrays.
!  * If the stavalues datatype is pass-by-reference, the values referenced by
!  * the values array are themselves palloc'd.  The palloc'd stuff can be
!  * freed by calling free_attstatsslot.
   *
!  * Note: at present, atttype/atttypmod aren't actually used here at all.
!  * But the caller must have the correct (or at least binary-compatible)
!  * type ID to pass to free_attstatsslot later.
   */
  bool
! get_attstatsslot(HeapTuple statstuple,
! 				 Oid atttype, int32 atttypmod,
! 				 int reqkind, Oid reqop,
! 				 Oid *actualop,
! 				 Datum **values, int *nvalues,
! 				 float4 **numbers, int *nnumbers)
  {
  	Form_pg_statistic stats = (Form_pg_statistic) GETSTRUCT(statstuple);
! 	int			i,
! 				j;
  	Datum		val;
  	bool		isnull;
  	ArrayType  *statarray;
--- 2864,2902 ----
   * that have been provided by a stats hook and didn't really come from
   * pg_statistic.
   *
+  * sslot: pointer to output area (typically, a local variable in the caller).
   * statstuple: pg_statistic tuple to be examined.
   * reqkind: STAKIND code for desired statistics slot kind.
   * reqop: STAOP value wanted, or InvalidOid if don't care.
!  * flags: bitmask of ATTSTATSSLOT_VALUES and/or ATTSTATSSLOT_NUMBERS.
   *
!  * If a matching slot is found, TRUE is returned, and *sslot is filled thus:
!  * staop: receives the actual STAOP value.
!  * valuetype: receives actual datatype of the elements of stavalues.
!  * values: receives pointer to an array of the slot's stavalues.
!  * nvalues: receives number of stavalues.
!  * numbers: receives pointer to an array of the slot's stanumbers (as float4).
!  * nnumbers: receives number of stanumbers.
   *
!  * valuetype/values/nvalues are InvalidOid/NULL/0 if ATTSTATSSLOT_VALUES
!  * wasn't specified.  Likewise, numbers/nnumbers are NULL/0 if
!  * ATTSTATSSLOT_NUMBERS wasn't specified.
!  *
!  * If no matching slot is found, FALSE is returned, and *sslot is zeroed.
!  *
!  * The data referred to by the fields of sslot is locally palloc'd and
!  * is independent of the original pg_statistic tuple.  When the caller
!  * is done with it, call free_attstatsslot to release the palloc'd data.
!  *
!  * If it's desirable to call free_attstatsslot when get_attstatsslot might
!  * not have been called, memset'ing sslot to zeroes will allow that.
   */
  bool
! get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
! 				 int reqkind, Oid reqop, int flags)
  {
  	Form_pg_statistic stats = (Form_pg_statistic) GETSTRUCT(statstuple);
! 	int			i;
  	Datum		val;
  	bool		isnull;
  	ArrayType  *statarray;
*************** get_attstatsslot(HeapTuple statstuple,
*** 2901,2906 ****
--- 2905,2913 ----
  	HeapTuple	typeTuple;
  	Form_pg_type typeForm;
  
+ 	/* initialize *sslot properly */
+ 	memset(sslot, 0, sizeof(AttStatsSlot));
+ 
  	for (i = 0; i < STATISTIC_NUM_SLOTS; i++)
  	{
  		if ((&stats->stakind1)[i] == reqkind &&
*************** get_attstatsslot(HeapTuple statstuple,
*** 2910,2935 ****
  	if (i >= STATISTIC_NUM_SLOTS)
  		return false;			/* not there */
  
! 	if (actualop)
! 		*actualop = (&stats->staop1)[i];
  
! 	if (values)
  	{
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stavalues1 + i,
  							  &isnull);
  		if (isnull)
  			elog(ERROR, "stavalues is null");
- 		statarray = DatumGetArrayTypeP(val);
  
  		/*
! 		 * Need to get info about the array element type.  We look at the
! 		 * actual element type embedded in the array, which might be only
! 		 * binary-compatible with the passed-in atttype.  The info we extract
! 		 * here should be the same either way, but deconstruct_array is picky
! 		 * about having an exact type OID match.
  		 */
! 		arrayelemtype = ARR_ELEMTYPE(statarray);
  		typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
  		if (!HeapTupleIsValid(typeTuple))
  			elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
--- 2917,2945 ----
  	if (i >= STATISTIC_NUM_SLOTS)
  		return false;			/* not there */
  
! 	sslot->staop = (&stats->staop1)[i];
  
! 	if (flags & ATTSTATSSLOT_VALUES)
  	{
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stavalues1 + i,
  							  &isnull);
  		if (isnull)
  			elog(ERROR, "stavalues is null");
  
  		/*
! 		 * Detoast the array if needed, and in any case make a copy that's
! 		 * under control of this AttStatsSlot.
  		 */
! 		statarray = DatumGetArrayTypePCopy(val);
! 
! 		/*
! 		 * Extract the actual array element type, and pass it back in case the
! 		 * caller needs it.
! 		 */
! 		sslot->valuetype = arrayelemtype = ARR_ELEMTYPE(statarray);
! 
! 		/* Need info about element type */
  		typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
  		if (!HeapTupleIsValid(typeTuple))
  			elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
*************** get_attstatsslot(HeapTuple statstuple,
*** 2941,2980 ****
  						  typeForm->typlen,
  						  typeForm->typbyval,
  						  typeForm->typalign,
! 						  values, NULL, nvalues);
  
  		/*
  		 * If the element type is pass-by-reference, we now have a bunch of
! 		 * Datums that are pointers into the syscache value.  Copy them to
! 		 * avoid problems if syscache decides to drop the entry.
  		 */
  		if (!typeForm->typbyval)
! 		{
! 			for (j = 0; j < *nvalues; j++)
! 			{
! 				(*values)[j] = datumCopy((*values)[j],
! 										 typeForm->typbyval,
! 										 typeForm->typlen);
! 			}
! 		}
  
  		ReleaseSysCache(typeTuple);
- 
- 		/*
- 		 * Free statarray if it's a detoasted copy.
- 		 */
- 		if ((Pointer) statarray != DatumGetPointer(val))
- 			pfree(statarray);
  	}
  
! 	if (numbers)
  	{
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stanumbers1 + i,
  							  &isnull);
  		if (isnull)
  			elog(ERROR, "stanumbers is null");
! 		statarray = DatumGetArrayTypeP(val);
  
  		/*
  		 * We expect the array to be a 1-D float4 array; verify that. We don't
--- 2951,2985 ----
  						  typeForm->typlen,
  						  typeForm->typbyval,
  						  typeForm->typalign,
! 						  &sslot->values, NULL, &sslot->nvalues);
  
  		/*
  		 * If the element type is pass-by-reference, we now have a bunch of
! 		 * Datums that are pointers into the statarray, so we need to keep
! 		 * that until free_attstatsslot.  Otherwise, all the useful info is in
! 		 * sslot->values[], so we can free the array object immediately.
  		 */
  		if (!typeForm->typbyval)
! 			sslot->values_arr = statarray;
! 		else
! 			pfree(statarray);
  
  		ReleaseSysCache(typeTuple);
  	}
  
! 	if (flags & ATTSTATSSLOT_NUMBERS)
  	{
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stanumbers1 + i,
  							  &isnull);
  		if (isnull)
  			elog(ERROR, "stanumbers is null");
! 
! 		/*
! 		 * Detoast the array if needed, and in any case make a copy that's
! 		 * under control of this AttStatsSlot.
! 		 */
! 		statarray = DatumGetArrayTypePCopy(val);
  
  		/*
  		 * We expect the array to be a 1-D float4 array; verify that. We don't
*************** get_attstatsslot(HeapTuple statstuple,
*** 2986,3000 ****
  			ARR_HASNULL(statarray) ||
  			ARR_ELEMTYPE(statarray) != FLOAT4OID)
  			elog(ERROR, "stanumbers is not a 1-D float4 array");
- 		*numbers = (float4 *) palloc(narrayelem * sizeof(float4));
- 		memcpy(*numbers, ARR_DATA_PTR(statarray), narrayelem * sizeof(float4));
- 		*nnumbers = narrayelem;
  
! 		/*
! 		 * Free statarray if it's a detoasted copy.
! 		 */
! 		if ((Pointer) statarray != DatumGetPointer(val))
! 			pfree(statarray);
  	}
  
  	return true;
--- 2991,3003 ----
  			ARR_HASNULL(statarray) ||
  			ARR_ELEMTYPE(statarray) != FLOAT4OID)
  			elog(ERROR, "stanumbers is not a 1-D float4 array");
  
! 		/* Give caller a pointer directly into the statarray */
! 		sslot->numbers = (float4 *) ARR_DATA_PTR(statarray);
! 		sslot->nnumbers = narrayelem;
! 
! 		/* We'll free the statarray in free_attstatsslot */
! 		sslot->numbers_arr = statarray;
  	}
  
  	return true;
*************** get_attstatsslot(HeapTuple statstuple,
*** 3003,3030 ****
  /*
   * free_attstatsslot
   *		Free data allocated by get_attstatsslot
-  *
-  * atttype is the type of the individual values in values[].
-  * It need be valid only if values != NULL.
   */
  void
! free_attstatsslot(Oid atttype,
! 				  Datum *values, int nvalues,
! 				  float4 *numbers, int nnumbers)
  {
! 	if (values)
! 	{
! 		if (!get_typbyval(atttype))
! 		{
! 			int			i;
! 
! 			for (i = 0; i < nvalues; i++)
! 				pfree(DatumGetPointer(values[i]));
! 		}
! 		pfree(values);
! 	}
! 	if (numbers)
! 		pfree(numbers);
  }
  
  /*				---------- PG_NAMESPACE CACHE ----------				 */
--- 3006,3024 ----
  /*
   * free_attstatsslot
   *		Free data allocated by get_attstatsslot
   */
  void
! free_attstatsslot(AttStatsSlot *sslot)
  {
! 	/* The values[] array was separately palloc'd by deconstruct_array */
! 	if (sslot->values)
! 		pfree(sslot->values);
! 	/* The numbers[] array points into numbers_arr, do not pfree it */
! 	/* Free the detoasted array objects, if any */
! 	if (sslot->values_arr)
! 		pfree(sslot->values_arr);
! 	if (sslot->numbers_arr)
! 		pfree(sslot->numbers_arr);
  }
  
  /*				---------- PG_NAMESPACE CACHE ----------				 */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 88629d9..a0d90e7 100644
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
*************** typedef enum IOFuncSelector
*** 35,40 ****
--- 35,62 ----
  	IOFunc_send
  } IOFuncSelector;
  
+ /* Flag bits for get_attstatsslot */
+ #define ATTSTATSSLOT_VALUES		0x01
+ #define ATTSTATSSLOT_NUMBERS	0x02
+ 
+ /* Result struct for get_attstatsslot */
+ typedef struct AttStatsSlot
+ {
+ 	/* Always filled: */
+ 	Oid			staop;			/* Actual staop for the found slot */
+ 	/* Filled if ATTSTATSSLOT_VALUES is specified: */
+ 	Oid			valuetype;		/* Actual datatype of the values */
+ 	Datum	   *values;			/* slot's "values" array, or NULL if none */
+ 	int			nvalues;		/* length of values[], or 0 */
+ 	/* Filled if ATTSTATSSLOT_NUMBERS is specified: */
+ 	float4	   *numbers;		/* slot's "numbers" array, or NULL if none */
+ 	int			nnumbers;		/* length of numbers[], or 0 */
+ 
+ 	/* Remaining fields are private to get_attstatsslot/free_attstatsslot */
+ 	void	   *values_arr;		/* palloc'd values array, if any */
+ 	void	   *numbers_arr;	/* palloc'd numbers array, if any */
+ } AttStatsSlot;
+ 
  /* Hook for plugins to get control in get_attavgwidth() */
  typedef int32 (*get_attavgwidth_hook_type) (Oid relid, AttrNumber attnum);
  extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook;
*************** extern Oid	getBaseType(Oid typid);
*** 148,162 ****
  extern Oid	getBaseTypeAndTypmod(Oid typid, int32 *typmod);
  extern int32 get_typavgwidth(Oid typid, int32 typmod);
  extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
! extern bool get_attstatsslot(HeapTuple statstuple,
! 				 Oid atttype, int32 atttypmod,
! 				 int reqkind, Oid reqop,
! 				 Oid *actualop,
! 				 Datum **values, int *nvalues,
! 				 float4 **numbers, int *nnumbers);
! extern void free_attstatsslot(Oid atttype,
! 				  Datum *values, int nvalues,
! 				  float4 *numbers, int nnumbers);
  extern char *get_namespace_name(Oid nspid);
  extern char *get_namespace_name_or_temp(Oid nspid);
  extern Oid	get_range_subtype(Oid rangeOid);
--- 170,178 ----
  extern Oid	getBaseTypeAndTypmod(Oid typid, int32 *typmod);
  extern int32 get_typavgwidth(Oid typid, int32 typmod);
  extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
! extern bool get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
! 				 int reqkind, Oid reqop, int flags);
! extern void free_attstatsslot(AttStatsSlot *sslot);
  extern char *get_namespace_name(Oid nspid);
  extern char *get_namespace_name_or_temp(Oid nspid);
  extern Oid	get_range_subtype(Oid rangeOid);
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index b7617dd..958bdba 100644
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef struct VariableStatData
*** 72,79 ****
  	/* NB: if statsTuple!=NULL, it must be freed when caller is done */
  	void		(*freefunc) (HeapTuple tuple);	/* how to free statsTuple */
  	Oid			vartype;		/* exposed type of expression */
! 	Oid			atttype;		/* type to pass to get_attstatsslot */
! 	int32		atttypmod;		/* typmod to pass to get_attstatsslot */
  	bool		isunique;		/* matches unique index or DISTINCT clause */
  	bool		acl_ok;			/* result of ACL check on table or column */
  } VariableStatData;
--- 72,79 ----
  	/* NB: if statsTuple!=NULL, it must be freed when caller is done */
  	void		(*freefunc) (HeapTuple tuple);	/* how to free statsTuple */
  	Oid			vartype;		/* exposed type of expression */
! 	Oid			atttype;		/* actual type (after stripping relabel) */
! 	int32		atttypmod;		/* actual typmod (after stripping relabel) */
  	bool		isunique;		/* matches unique index or DISTINCT clause */
  	bool		acl_ok;			/* result of ACL check on table or column */
  } VariableStatData;

-- 
Sent via pgsql-hackers mailing list ([email-masked])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

---

From: Simon Riggs <[email-masked]>
Date: Thu, 11 May 2017 17:49:32 +0100

On 11 May 2017 at 17:41, Tom Lane <[email-masked]> wrote:
> ...because code that worked fine for Peter and me failed
> erratically in the buildfarm.

I think its always a little bit too exciting for me also.

I suggest we have a commit tree and a main tree, with automatic
copying from commit -> main either
1. 24 hours after commit
2. or earlier if we have a full set of green results from people
running the full suite on the commit tree

That way we don't have to polute the main tree with all this jumping around.

Many alternate ideas possible.

-- 
Simon Riggs                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

---

From: Tom Lane <[email-masked]>
Date: Thu, 11 May 2017 13:20:57 -0400

> running the full suite on the commit tree

Meh.  We don't really need that in normal development, and for security
patches there's still a problem: we don't want to wait around 24 hours
after the code is public.

			regards, tom lane