diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 18c38e4..af4d062 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
*************** SCRAM-SHA-256$<replaceable>&lt;iteration
*** 6395,6400 ****
--- 6395,6412 ----
       </row>
  
       <row>
+       <entry><structfield>stacoll<replaceable>N</replaceable></structfield></entry>
+       <entry><type>oid</type></entry>
+       <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+       <entry>
+        The collation used to derive the statistics stored in the
+        <replaceable>N</replaceable>th <quote>slot</quote>.  For example, a
+        histogram slot for a collatable column would show the collation that
+        defines the sort order of the data.  Zero for noncollatable data.
+       </entry>
+      </row>
+ 
+      <row>
        <entry><structfield>stanumbers<replaceable>N</replaceable></structfield></entry>
        <entry><type>float4[]</type></entry>
        <entry></entry>
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b8445dc..dcbd04c 100644
*** a/src/backend/commands/analyze.c
--- b/src/backend/commands/analyze.c
*************** examine_attribute(Relation onerel, int a
*** 904,914 ****
--- 904,917 ----
  	{
  		stats->attrtypid = exprType(index_expr);
  		stats->attrtypmod = exprTypmod(index_expr);
+ 		stats->attrcollid = exprCollation(index_expr);
+ 		/* XXX should we consult indcollation instead? */
  	}
  	else
  	{
  		stats->attrtypid = attr->atttypid;
  		stats->attrtypmod = attr->atttypmod;
+ 		stats->attrcollid = attr->attcollation;
  	}
  
  	typtuple = SearchSysCacheCopy1(TYPEOID,
*************** update_attstats(Oid relid, bool inh, int
*** 1553,1558 ****
--- 1556,1566 ----
  		{
  			values[i++] = ObjectIdGetDatum(stats->staop[k]);	/* staopN */
  		}
+ 		i = Anum_pg_statistic_stacoll1 - 1;
+ 		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
+ 		{
+ 			values[i++] = ObjectIdGetDatum(stats->stacoll[k]);	/* stacollN */
+ 		}
  		i = Anum_pg_statistic_stanumbers1 - 1;
  		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
  		{
*************** compute_distinct_stats(VacAttrStatsP sta
*** 1993,2001 ****
  		firstcount1 = track_cnt;
  		for (j = 0; j < track_cnt; j++)
  		{
- 			/* We always use the default collation for statistics */
  			if (DatumGetBool(FunctionCall2Coll(&f_cmpeq,
! 											   DEFAULT_COLLATION_OID,
  											   value, track[j].value)))
  			{
  				match = true;
--- 2001,2008 ----
  		firstcount1 = track_cnt;
  		for (j = 0; j < track_cnt; j++)
  		{
  			if (DatumGetBool(FunctionCall2Coll(&f_cmpeq,
! 											   stats->attrcollid,
  											   value, track[j].value)))
  			{
  				match = true;
*************** compute_distinct_stats(VacAttrStatsP sta
*** 2202,2207 ****
--- 2209,2215 ----
  
  			stats->stakind[0] = STATISTIC_KIND_MCV;
  			stats->staop[0] = mystats->eqopr;
+ 			stats->stacoll[0] = stats->attrcollid;
  			stats->stanumbers[0] = mcv_freqs;
  			stats->numnumbers[0] = num_mcv;
  			stats->stavalues[0] = mcv_values;
*************** compute_scalar_stats(VacAttrStatsP stats
*** 2273,2280 ****
  
  	memset(&ssup, 0, sizeof(ssup));
  	ssup.ssup_cxt = CurrentMemoryContext;
! 	/* We always use the default collation for statistics */
! 	ssup.ssup_collation = DEFAULT_COLLATION_OID;
  	ssup.ssup_nulls_first = false;
  
  	/*
--- 2281,2287 ----
  
  	memset(&ssup, 0, sizeof(ssup));
  	ssup.ssup_cxt = CurrentMemoryContext;
! 	ssup.ssup_collation = stats->attrcollid;
  	ssup.ssup_nulls_first = false;
  
  	/*
*************** compute_scalar_stats(VacAttrStatsP stats
*** 2567,2572 ****
--- 2574,2580 ----
  
  			stats->stakind[slot_idx] = STATISTIC_KIND_MCV;
  			stats->staop[slot_idx] = mystats->eqopr;
+ 			stats->stacoll[slot_idx] = stats->attrcollid;
  			stats->stanumbers[slot_idx] = mcv_freqs;
  			stats->numnumbers[slot_idx] = num_mcv;
  			stats->stavalues[slot_idx] = mcv_values;
*************** compute_scalar_stats(VacAttrStatsP stats
*** 2682,2687 ****
--- 2690,2696 ----
  
  			stats->stakind[slot_idx] = STATISTIC_KIND_HISTOGRAM;
  			stats->staop[slot_idx] = mystats->ltopr;
+ 			stats->stacoll[slot_idx] = stats->attrcollid;
  			stats->stavalues[slot_idx] = hist_values;
  			stats->numvalues[slot_idx] = num_hist;
  
*************** compute_scalar_stats(VacAttrStatsP stats
*** 2725,2730 ****
--- 2734,2740 ----
  
  			stats->stakind[slot_idx] = STATISTIC_KIND_CORRELATION;
  			stats->staop[slot_idx] = mystats->ltopr;
+ 			stats->stacoll[slot_idx] = stats->attrcollid;
  			stats->stanumbers[slot_idx] = corrs;
  			stats->numnumbers[slot_idx] = 1;
  			slot_idx++;
diff --git a/src/backend/tsearch/ts_typanalyze.c b/src/backend/tsearch/ts_typanalyze.c
index 1f93963..bd34711 100644
*** a/src/backend/tsearch/ts_typanalyze.c
--- b/src/backend/tsearch/ts_typanalyze.c
***************
*** 14,19 ****
--- 14,20 ----
  #include "postgres.h"
  
  #include "access/hash.h"
+ #include "catalog/pg_collation.h"
  #include "catalog/pg_operator.h"
  #include "commands/vacuum.h"
  #include "tsearch/ts_type.h"
*************** compute_tsvector_stats(VacAttrStats *sta
*** 415,420 ****
--- 416,422 ----
  
  			stats->stakind[0] = STATISTIC_KIND_MCELEM;
  			stats->staop[0] = TextEqualOperator;
+ 			stats->stacoll[0] = DEFAULT_COLLATION_OID;
  			stats->stanumbers[0] = mcelem_freqs;
  			/* See above comment about two extra frequency fields */
  			stats->numnumbers[0] = num_mcelem + 2;
diff --git a/src/backend/utils/adt/array_typanalyze.c b/src/backend/utils/adt/array_typanalyze.c
index 92e38b8..c4a1fef 100644
*** a/src/backend/utils/adt/array_typanalyze.c
--- b/src/backend/utils/adt/array_typanalyze.c
***************
*** 15,21 ****
  #include "postgres.h"
  
  #include "access/tuptoaster.h"
- #include "catalog/pg_collation.h"
  #include "commands/vacuum.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
--- 15,20 ----
*************** typedef struct
*** 39,44 ****
--- 38,44 ----
  	/* Information about array element type */
  	Oid			type_id;		/* element type's OID */
  	Oid			eq_opr;			/* default equality operator's OID */
+ 	Oid			coll_id;		/* collation to use */
  	bool		typbyval;		/* physical properties of element type */
  	int16		typlen;
  	char		typalign;
*************** array_typanalyze(PG_FUNCTION_ARGS)
*** 135,140 ****
--- 135,141 ----
  	extra_data = (ArrayAnalyzeExtraData *) palloc(sizeof(ArrayAnalyzeExtraData));
  	extra_data->type_id = typentry->type_id;
  	extra_data->eq_opr = typentry->eq_opr;
+ 	extra_data->coll_id = stats->attrcollid;	/* collation we should use */
  	extra_data->typbyval = typentry->typbyval;
  	extra_data->typlen = typentry->typlen;
  	extra_data->typalign = typentry->typalign;
*************** compute_array_stats(VacAttrStats *stats,
*** 560,565 ****
--- 561,567 ----
  
  			stats->stakind[slot_idx] = STATISTIC_KIND_MCELEM;
  			stats->staop[slot_idx] = extra_data->eq_opr;
+ 			stats->stacoll[slot_idx] = extra_data->coll_id;
  			stats->stanumbers[slot_idx] = mcelem_freqs;
  			/* See above comment about extra stanumber entries */
  			stats->numnumbers[slot_idx] = num_mcelem + 3;
*************** compute_array_stats(VacAttrStats *stats,
*** 661,666 ****
--- 663,669 ----
  
  			stats->stakind[slot_idx] = STATISTIC_KIND_DECHIST;
  			stats->staop[slot_idx] = extra_data->eq_opr;
+ 			stats->stacoll[slot_idx] = extra_data->coll_id;
  			stats->stanumbers[slot_idx] = hist;
  			stats->numnumbers[slot_idx] = num_hist + 1;
  			slot_idx++;
*************** prune_element_hashtable(HTAB *elements_t
*** 703,709 ****
  /*
   * Hash function for elements.
   *
!  * We use the element type's default hash opclass, and the default collation
   * if the type is collation-sensitive.
   */
  static uint32
--- 706,712 ----
  /*
   * Hash function for elements.
   *
!  * We use the element type's default hash opclass, and the column collation
   * if the type is collation-sensitive.
   */
  static uint32
*************** element_hash(const void *key, Size keysi
*** 712,718 ****
  	Datum		d = *((const Datum *) key);
  	Datum		h;
  
! 	h = FunctionCall1Coll(array_extra_data->hash, DEFAULT_COLLATION_OID, d);
  	return DatumGetUInt32(h);
  }
  
--- 715,723 ----
  	Datum		d = *((const Datum *) key);
  	Datum		h;
  
! 	h = FunctionCall1Coll(array_extra_data->hash,
! 						  array_extra_data->coll_id,
! 						  d);
  	return DatumGetUInt32(h);
  }
  
*************** element_match(const void *key1, const vo
*** 729,735 ****
  /*
   * Comparison function for elements.
   *
!  * We use the element type's default btree opclass, and the default collation
   * if the type is collation-sensitive.
   *
   * XXX consider using SortSupport infrastructure
--- 734,740 ----
  /*
   * Comparison function for elements.
   *
!  * We use the element type's default btree opclass, and the column collation
   * if the type is collation-sensitive.
   *
   * XXX consider using SortSupport infrastructure
*************** element_compare(const void *key1, const 
*** 741,747 ****
  	Datum		d2 = *((const Datum *) key2);
  	Datum		c;
  
! 	c = FunctionCall2Coll(array_extra_data->cmp, DEFAULT_COLLATION_OID, d1, d2);
  	return DatumGetInt32(c);
  }
  
--- 746,754 ----
  	Datum		d2 = *((const Datum *) key2);
  	Datum		c;
  
! 	c = FunctionCall2Coll(array_extra_data->cmp,
! 						  array_extra_data->coll_id,
! 						  d1, d2);
  	return DatumGetInt32(c);
  }
  
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index 9c50e4c..98cf5f8 100644
*** a/src/backend/utils/adt/rangetypes_typanalyze.c
--- b/src/backend/utils/adt/rangetypes_typanalyze.c
*************** compute_range_stats(VacAttrStats *stats,
*** 320,325 ****
--- 320,326 ----
  			num_hist = 0;
  		}
  		stats->staop[slot_idx] = Float8LessOperator;
+ 		stats->stacoll[slot_idx] = InvalidOid;
  		stats->stavalues[slot_idx] = length_hist_values;
  		stats->numvalues[slot_idx] = num_hist;
  		stats->statypid[slot_idx] = FLOAT8OID;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index ffca0fe..c3db9ea 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
***************
*** 87,97 ****
   * For both oprrest and oprjoin functions, the operator's input collation OID
   * (if any) is passed using the standard fmgr mechanism, so that the estimator
   * function can fetch it with PG_GET_COLLATION().  Note, however, that all
!  * statistics in pg_statistic are currently built using the database's default
   * collation.  Thus, in most cases where we are looking at statistics, we
!  * should ignore the actual operator collation and use DEFAULT_COLLATION_OID.
   * We expect that the error induced by doing this is usually not large enough
!  * to justify complicating matters.
   *----------
   */
  
--- 87,98 ----
   * For both oprrest and oprjoin functions, the operator's input collation OID
   * (if any) is passed using the standard fmgr mechanism, so that the estimator
   * function can fetch it with PG_GET_COLLATION().  Note, however, that all
!  * statistics in pg_statistic are currently built using the relevant column's
   * collation.  Thus, in most cases where we are looking at statistics, we
!  * should ignore the operator collation and use the stats entry's collation.
   * We expect that the error induced by doing this is usually not large enough
!  * to justify complicating matters.  In any case, doing otherwise would yield
!  * entirely garbage results for ordered stats data such as histograms.
   *----------
   */
  
*************** static double eqjoinsel_semi(Oid opfunco
*** 181,187 ****
  			   RelOptInfo *inner_rel);
  static bool estimate_multivariate_ndistinct(PlannerInfo *root,
  								RelOptInfo *rel, List **varinfos, double *ndistinct);
! static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
  				  Datum lobound, Datum hibound, Oid boundstypid,
  				  double *scaledlobound, double *scaledhibound);
  static double convert_numeric_to_scalar(Datum value, Oid typid, bool *failure);
--- 182,189 ----
  			   RelOptInfo *inner_rel);
  static bool estimate_multivariate_ndistinct(PlannerInfo *root,
  								RelOptInfo *rel, List **varinfos, double *ndistinct);
! static bool convert_to_scalar(Datum value, Oid valuetypid, Oid collid,
! 				  double *scaledvalue,
  				  Datum lobound, Datum hibound, Oid boundstypid,
  				  double *scaledlobound, double *scaledhibound);
  static double convert_numeric_to_scalar(Datum value, Oid typid, bool *failure);
*************** static double convert_one_string_to_scal
*** 201,207 ****
  							 int rangelo, int rangehi);
  static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
  							int rangelo, int rangehi);
! static char *convert_string_datum(Datum value, Oid typid, bool *failure);
  static double convert_timevalue_to_scalar(Datum value, Oid typid,
  							bool *failure);
  static void examine_simple_variable(PlannerInfo *root, Var *var,
--- 203,210 ----
  							 int rangelo, int rangehi);
  static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
  							int rangelo, int rangehi);
! static char *convert_string_datum(Datum value, Oid typid, Oid collid,
! 					 bool *failure);
  static double convert_timevalue_to_scalar(Datum value, Oid typid,
  							bool *failure);
  static void examine_simple_variable(PlannerInfo *root, Var *var,
*************** var_eq_const(VariableStatData *vardata, 
*** 370,381 ****
  				/* 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)
--- 373,384 ----
  				/* be careful to apply operator right way 'round */
  				if (varonleft)
  					match = DatumGetBool(FunctionCall2Coll(&eqproc,
! 														   sslot.stacoll,
  														   sslot.values[i],
  														   constval));
  				else
  					match = DatumGetBool(FunctionCall2Coll(&eqproc,
! 														   sslot.stacoll,
  														   constval,
  														   sslot.values[i]));
  				if (match)
*************** mcv_selectivity(VariableStatData *vardat
*** 666,676 ****
  		{
  			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];
--- 669,679 ----
  		{
  			if (varonleft ?
  				DatumGetBool(FunctionCall2Coll(opproc,
! 											   sslot.stacoll,
  											   sslot.values[i],
  											   constval)) :
  				DatumGetBool(FunctionCall2Coll(opproc,
! 											   sslot.stacoll,
  											   constval,
  											   sslot.values[i])))
  				mcv_selec += sslot.numbers[i];
*************** histogram_selectivity(VariableStatData *
*** 744,754 ****
  			{
  				if (varonleft ?
  					DatumGetBool(FunctionCall2Coll(opproc,
! 												   DEFAULT_COLLATION_OID,
  												   sslot.values[i],
  												   constval)) :
  					DatumGetBool(FunctionCall2Coll(opproc,
! 												   DEFAULT_COLLATION_OID,
  												   constval,
  												   sslot.values[i])))
  					nmatch++;
--- 747,757 ----
  			{
  				if (varonleft ?
  					DatumGetBool(FunctionCall2Coll(opproc,
! 												   sslot.stacoll,
  												   sslot.values[i],
  												   constval)) :
  					DatumGetBool(FunctionCall2Coll(opproc,
! 												   sslot.stacoll,
  												   constval,
  												   sslot.values[i])))
  					nmatch++;
*************** ineq_histogram_selectivity(PlannerInfo *
*** 873,879 ****
  														 &sslot.values[probe]);
  
  				ltcmp = DatumGetBool(FunctionCall2Coll(opproc,
! 													   DEFAULT_COLLATION_OID,
  													   sslot.values[probe],
  													   constval));
  				if (isgt)
--- 876,882 ----
  														 &sslot.values[probe]);
  
  				ltcmp = DatumGetBool(FunctionCall2Coll(opproc,
! 													   sslot.stacoll,
  													   sslot.values[probe],
  													   constval));
  				if (isgt)
*************** ineq_histogram_selectivity(PlannerInfo *
*** 958,964 ****
  				 * values to a uniform comparison scale, and do a linear
  				 * interpolation within this bin.
  				 */
! 				if (convert_to_scalar(constval, consttype, &val,
  									  sslot.values[i - 1], sslot.values[i],
  									  vardata->vartype,
  									  &low, &high))
--- 961,968 ----
  				 * values to a uniform comparison scale, and do a linear
  				 * interpolation within this bin.
  				 */
! 				if (convert_to_scalar(constval, consttype, sslot.stacoll,
! 									  &val,
  									  sslot.values[i - 1], sslot.values[i],
  									  vardata->vartype,
  									  &low, &high))
*************** eqjoinsel_inner(Oid opfuncoid,
*** 2499,2505 ****
  				if (hasmatch2[j])
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
! 												   DEFAULT_COLLATION_OID,
  												   sslot1->values[i],
  												   sslot2->values[j])))
  				{
--- 2503,2509 ----
  				if (hasmatch2[j])
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
! 												   sslot1->stacoll,
  												   sslot1->values[i],
  												   sslot2->values[j])))
  				{
*************** eqjoinsel_semi(Oid opfuncoid,
*** 2711,2717 ****
  				if (hasmatch2[j])
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
! 												   DEFAULT_COLLATION_OID,
  												   sslot1->values[i],
  												   sslot2->values[j])))
  				{
--- 2715,2721 ----
  				if (hasmatch2[j])
  					continue;
  				if (DatumGetBool(FunctionCall2Coll(&eqproc,
! 												   sslot1->stacoll,
  												   sslot1->values[i],
  												   sslot2->values[j])))
  				{
*************** estimate_multivariate_ndistinct(PlannerI
*** 4066,4072 ****
   * converted to measurements expressed in seconds.
   */
  static bool
! convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
  				  Datum lobound, Datum hibound, Oid boundstypid,
  				  double *scaledlobound, double *scaledhibound)
  {
--- 4070,4076 ----
   * converted to measurements expressed in seconds.
   */
  static bool
! convert_to_scalar(Datum value, Oid valuetypid, Oid collid, double *scaledvalue,
  				  Datum lobound, Datum hibound, Oid boundstypid,
  				  double *scaledlobound, double *scaledhibound)
  {
*************** convert_to_scalar(Datum value, Oid value
*** 4131,4141 ****
  		case NAMEOID:
  			{
  				char	   *valstr = convert_string_datum(value, valuetypid,
! 														  &failure);
  				char	   *lostr = convert_string_datum(lobound, boundstypid,
! 														 &failure);
  				char	   *histr = convert_string_datum(hibound, boundstypid,
! 														 &failure);
  
  				/*
  				 * Bail out if any of the values is not of string type.  We
--- 4135,4145 ----
  		case NAMEOID:
  			{
  				char	   *valstr = convert_string_datum(value, valuetypid,
! 														  collid, &failure);
  				char	   *lostr = convert_string_datum(lobound, boundstypid,
! 														 collid, &failure);
  				char	   *histr = convert_string_datum(hibound, boundstypid,
! 														 collid, &failure);
  
  				/*
  				 * Bail out if any of the values is not of string type.  We
*************** convert_one_string_to_scalar(char *value
*** 4404,4410 ****
   * before continuing, so as to generate correct locale-specific results.
   */
  static char *
! convert_string_datum(Datum value, Oid typid, bool *failure)
  {
  	char	   *val;
  
--- 4408,4414 ----
   * before continuing, so as to generate correct locale-specific results.
   */
  static char *
! convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure)
  {
  	char	   *val;
  
*************** convert_string_datum(Datum value, Oid ty
*** 4432,4438 ****
  			return NULL;
  	}
  
! 	if (!lc_collate_is_c(DEFAULT_COLLATION_OID))
  	{
  		char	   *xfrmstr;
  		size_t		xfrmlen;
--- 4436,4442 ----
  			return NULL;
  	}
  
! 	if (!lc_collate_is_c(collid))
  	{
  		char	   *xfrmstr;
  		size_t		xfrmlen;
*************** get_variable_range(PlannerInfo *root, Va
*** 5407,5420 ****
  				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];
--- 5411,5424 ----
  				continue;
  			}
  			if (DatumGetBool(FunctionCall2Coll(&opproc,
! 											   sslot.stacoll,
  											   sslot.values[i], tmin)))
  			{
  				tmin = sslot.values[i];
  				tmin_is_mcv = true;
  			}
  			if (DatumGetBool(FunctionCall2Coll(&opproc,
! 											   sslot.stacoll,
  											   tmax, sslot.values[i])))
  			{
  				tmax = sslot.values[i];
*************** prefix_selectivity(PlannerInfo *root, Va
*** 6014,6019 ****
--- 6018,6024 ----
  	Selectivity prefixsel;
  	Oid			cmpopr;
  	FmgrInfo	opproc;
+ 	AttStatsSlot sslot;
  	Const	   *greaterstrcon;
  	Selectivity eq_sel;
  
*************** prefix_selectivity(PlannerInfo *root, Va
*** 6036,6051 ****
  
  	/*-------
  	 * If we can create a string larger than the prefix, say
! 	 *	"x < greaterstr".
  	 *-------
  	 */
  	cmpopr = get_opfamily_member(opfamily, vartype, vartype,
  								 BTLessStrategyNumber);
  	if (cmpopr == InvalidOid)
  		elog(ERROR, "no < operator for opfamily %u", opfamily);
  	fmgr_info(get_opcode(cmpopr), &opproc);
! 	greaterstrcon = make_greater_string(prefixcon, &opproc,
! 										DEFAULT_COLLATION_OID);
  	if (greaterstrcon)
  	{
  		Selectivity topsel;
--- 6041,6063 ----
  
  	/*-------
  	 * If we can create a string larger than the prefix, say
! 	 * "x < greaterstr".  We try to generate the string referencing the
! 	 * collation of the var's statistics, but if that's not available,
! 	 * use DEFAULT_COLLATION_OID.
  	 *-------
  	 */
+ 	if (HeapTupleIsValid(vardata->statsTuple) &&
+ 		get_attstatsslot(&sslot, vardata->statsTuple,
+ 						 STATISTIC_KIND_HISTOGRAM, InvalidOid, 0))
+ 		 /* sslot.stacoll is set up */ ;
+ 	else
+ 		sslot.stacoll = DEFAULT_COLLATION_OID;
  	cmpopr = get_opfamily_member(opfamily, vartype, vartype,
  								 BTLessStrategyNumber);
  	if (cmpopr == InvalidOid)
  		elog(ERROR, "no < operator for opfamily %u", opfamily);
  	fmgr_info(get_opcode(cmpopr), &opproc);
! 	greaterstrcon = make_greater_string(prefixcon, &opproc, sslot.stacoll);
  	if (greaterstrcon)
  	{
  		Selectivity topsel;
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 7a263cc..33b5b16 100644
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
*************** get_attavgwidth(Oid relid, AttrNumber at
*** 2881,2886 ****
--- 2881,2887 ----
   *
   * If a matching slot is found, true is returned, and *sslot is filled thus:
   * staop: receives the actual STAOP value.
+  * stacoll: receives the actual STACOLL 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.
*************** get_attavgwidth(Oid relid, AttrNumber at
*** 2893,2898 ****
--- 2894,2903 ----
   *
   * If no matching slot is found, false is returned, and *sslot is zeroed.
   *
+  * Note that the current API doesn't allow for searching for a slot with
+  * a particular collation.  If we ever actually support recording more than
+  * one collation, we'll have to extend the API, but for now simple is good.
+  *
   * 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.
*************** get_attstatsslot(AttStatsSlot *sslot, He
*** 2927,2932 ****
--- 2932,2951 ----
  		return false;			/* not there */
  
  	sslot->staop = (&stats->staop1)[i];
+ 	sslot->stacoll = (&stats->stacoll1)[i];
+ 
+ 	/*
+ 	 * XXX Hopefully-temporary hack: if stacoll isn't set, inject the default
+ 	 * collation.  This won't matter for non-collation-aware datatypes.  For
+ 	 * those that are, this covers cases where stacoll has not been set.  In
+ 	 * the short term we need this because some code paths involving type NAME
+ 	 * do not pass any collation to prefix_selectivity and related functions.
+ 	 * Even when that's been fixed, it's likely that some add-on typanalyze
+ 	 * functions won't get the word right away about filling stacoll during
+ 	 * ANALYZE, so we'll probably need this for awhile.
+ 	 */
+ 	if (sslot->stacoll == InvalidOid)
+ 		sslot->stacoll = DEFAULT_COLLATION_OID;
  
  	if (flags & ATTSTATSSLOT_VALUES)
  	{
diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h
index 49223aa..2155f51 100644
*** a/src/include/catalog/pg_statistic.h
--- b/src/include/catalog/pg_statistic.h
*************** CATALOG(pg_statistic,2619,StatisticRelat
*** 74,85 ****
  	 * statistical data can be placed.  Each slot includes:
  	 *		kind			integer code identifying kind of data (see below)
  	 *		op				OID of associated operator, if needed
  	 *		numbers			float4 array (for statistical values)
  	 *		values			anyarray (for representations of data values)
! 	 * The ID and operator fields are never NULL; they are zeroes in an
! 	 * unused slot.  The numbers and values fields are NULL in an unused
! 	 * slot, and might also be NULL in a used slot if the slot kind has
! 	 * no need for one or the other.
  	 * ----------------
  	 */
  
--- 74,86 ----
  	 * statistical data can be placed.  Each slot includes:
  	 *		kind			integer code identifying kind of data (see below)
  	 *		op				OID of associated operator, if needed
+ 	 *		coll			OID of relevant collation, or 0 if none
  	 *		numbers			float4 array (for statistical values)
  	 *		values			anyarray (for representations of data values)
! 	 * The ID, operator, and collation fields are never NULL; they are zeroes
! 	 * in an unused slot.  The numbers and values fields are NULL in an
! 	 * unused slot, and might also be NULL in a used slot if the slot kind
! 	 * has no need for one or the other.
  	 * ----------------
  	 */
  
*************** CATALOG(pg_statistic,2619,StatisticRelat
*** 95,100 ****
--- 96,107 ----
  	Oid			staop4;
  	Oid			staop5;
  
+ 	Oid			stacoll1;
+ 	Oid			stacoll2;
+ 	Oid			stacoll3;
+ 	Oid			stacoll4;
+ 	Oid			stacoll5;
+ 
  #ifdef CATALOG_VARLEN			/* variable-length fields start here */
  	float4		stanumbers1[1];
  	float4		stanumbers2[1];
*************** typedef FormData_pg_statistic *Form_pg_s
*** 159,165 ****
  
  /*
   * In a "most common values" slot, staop is the OID of the "=" operator
!  * used to decide whether values are the same or not.  stavalues contains
   * the K most common non-null values appearing in the column, and stanumbers
   * contains their frequencies (fractions of total row count).  The values
   * shall be ordered in decreasing frequency.  Note that since the arrays are
--- 166,173 ----
  
  /*
   * In a "most common values" slot, staop is the OID of the "=" operator
!  * used to decide whether values are the same or not, and stacoll is the
!  * collation used (same as column's collation).  stavalues contains
   * the K most common non-null values appearing in the column, and stanumbers
   * contains their frequencies (fractions of total row count).  The values
   * shall be ordered in decreasing frequency.  Note that since the arrays are
*************** typedef FormData_pg_statistic *Form_pg_s
*** 171,179 ****
  
  /*
   * A "histogram" slot describes the distribution of scalar data.  staop is
!  * the OID of the "<" operator that describes the sort ordering.  (In theory,
!  * more than one histogram could appear, if a datatype has more than one
!  * useful sort operator.)  stavalues contains M (>=2) non-null values that
   * divide the non-null column data values into M-1 bins of approximately equal
   * population.  The first stavalues item is the MIN and the last is the MAX.
   * stanumbers is not used and should be NULL.  IMPORTANT POINT: if an MCV
--- 179,189 ----
  
  /*
   * A "histogram" slot describes the distribution of scalar data.  staop is
!  * the OID of the "<" operator that describes the sort ordering, and stacoll
!  * is the relevant collation.  (In theory more than one histogram could appear,
!  * if a datatype has more than one useful sort operator or we care about more
!  * than one collation.  Currently the collation will always be that of the
!  * underlying column.)  stavalues contains M (>=2) non-null values that
   * divide the non-null column data values into M-1 bins of approximately equal
   * population.  The first stavalues item is the MIN and the last is the MAX.
   * stanumbers is not used and should be NULL.  IMPORTANT POINT: if an MCV
*************** typedef FormData_pg_statistic *Form_pg_s
*** 190,200 ****
  /*
   * A "correlation" slot describes the correlation between the physical order
   * of table tuples and the ordering of data values of this column, as seen
!  * by the "<" operator identified by staop.  (As with the histogram, more
!  * than one entry could theoretically appear.)	stavalues is not used and
!  * should be NULL.  stanumbers contains a single entry, the correlation
!  * coefficient between the sequence of data values and the sequence of
!  * their actual tuple positions.  The coefficient ranges from +1 to -1.
   */
  #define STATISTIC_KIND_CORRELATION	3
  
--- 200,211 ----
  /*
   * A "correlation" slot describes the correlation between the physical order
   * of table tuples and the ordering of data values of this column, as seen
!  * by the "<" operator identified by staop with the collation identified by
!  * stacoll.  (As with the histogram, more than one entry could theoretically
!  * appear.)  stavalues is not used and should be NULL.  stanumbers contains
!  * a single entry, the correlation coefficient between the sequence of data
!  * values and the sequence of their actual tuple positions.  The coefficient
!  * ranges from +1 to -1.
   */
  #define STATISTIC_KIND_CORRELATION	3
  
*************** typedef FormData_pg_statistic *Form_pg_s
*** 203,209 ****
   * except that it stores the most common non-null *elements* of the column
   * values.  This is useful when the column datatype is an array or some other
   * type with identifiable elements (for instance, tsvector).  staop contains
!  * the equality operator appropriate to the element type.  stavalues contains
   * the most common element values, and stanumbers their frequencies.  Unlike
   * MCV slots, frequencies are measured as the fraction of non-null rows the
   * element value appears in, not the frequency of all rows.  Also unlike
--- 214,221 ----
   * except that it stores the most common non-null *elements* of the column
   * values.  This is useful when the column datatype is an array or some other
   * type with identifiable elements (for instance, tsvector).  staop contains
!  * the equality operator appropriate to the element type, and stacoll
!  * contains the collation to use with it.  stavalues contains
   * the most common element values, and stanumbers their frequencies.  Unlike
   * MCV slots, frequencies are measured as the fraction of non-null rows the
   * element value appears in, not the frequency of all rows.  Also unlike
*************** typedef FormData_pg_statistic *Form_pg_s
*** 226,232 ****
   * A "distinct elements count histogram" slot describes the distribution of
   * the number of distinct element values present in each row of an array-type
   * column.  Only non-null rows are considered, and only non-null elements.
!  * staop contains the equality operator appropriate to the element type.
   * stavalues is not used and should be NULL.  The last member of stanumbers is
   * the average count of distinct element values over all non-null rows.  The
   * preceding M (>=2) members form a histogram that divides the population of
--- 238,245 ----
   * A "distinct elements count histogram" slot describes the distribution of
   * the number of distinct element values present in each row of an array-type
   * column.  Only non-null rows are considered, and only non-null elements.
!  * staop contains the equality operator appropriate to the element type,
!  * and stacoll contains the collation to use with it.
   * stavalues is not used and should be NULL.  The last member of stanumbers is
   * the average count of distinct element values over all non-null rows.  The
   * preceding M (>=2) members form a histogram that divides the population of
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 2f4303e..dfff23a 100644
*** a/src/include/commands/vacuum.h
--- b/src/include/commands/vacuum.h
***************
*** 52,60 ****
   * careful to allocate any pointed-to data in anl_context, which will NOT
   * be CurrentMemoryContext when compute_stats is called.
   *
!  * Note: for the moment, all comparisons done for statistical purposes
!  * should use the database's default collation (DEFAULT_COLLATION_OID).
!  * This might change in some future release.
   *----------
   */
  typedef struct VacAttrStats *VacAttrStatsP;
--- 52,62 ----
   * careful to allocate any pointed-to data in anl_context, which will NOT
   * be CurrentMemoryContext when compute_stats is called.
   *
!  * Note: all comparisons done for statistical purposes should use the
!  * underlying column's collation (attcollation), except in situations
!  * where a noncollatable container type contains a collatable type;
!  * in that case use the type's default collation.  Be sure to record
!  * the appropriate collation in stacoll.
   *----------
   */
  typedef struct VacAttrStats *VacAttrStatsP;
*************** typedef struct VacAttrStats
*** 78,88 ****
--- 80,92 ----
  	 * because some index opclasses store a different type than the underlying
  	 * column/expression.  Instead use attrtypid, attrtypmod, and attrtype for
  	 * information about the datatype being fed to the typanalyze function.
+ 	 * Likewise, use attrcollid not attr->attcollation.
  	 */
  	Form_pg_attribute attr;		/* copy of pg_attribute row for column */
  	Oid			attrtypid;		/* type of data being analyzed */
  	int32		attrtypmod;		/* typmod of data being analyzed */
  	Form_pg_type attrtype;		/* copy of pg_type row for attrtypid */
+ 	Oid			attrcollid;		/* collation of data being analyzed */
  	MemoryContext anl_context;	/* where to save long-lived data */
  
  	/*
*************** typedef struct VacAttrStats
*** 103,108 ****
--- 107,113 ----
  	float4		stadistinct;	/* # distinct values */
  	int16		stakind[STATISTIC_NUM_SLOTS];
  	Oid			staop[STATISTIC_NUM_SLOTS];
+ 	Oid			stacoll[STATISTIC_NUM_SLOTS];
  	int			numnumbers[STATISTIC_NUM_SLOTS];
  	float4	   *stanumbers[STATISTIC_NUM_SLOTS];
  	int			numvalues[STATISTIC_NUM_SLOTS];
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index ff1705a..6408993 100644
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
*************** typedef struct AttStatsSlot
*** 44,49 ****
--- 44,50 ----
  {
  	/* Always filled: */
  	Oid			staop;			/* Actual staop for the found slot */
+ 	Oid			stacoll;		/* Actual collation 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 */
