attnum-int2vector.patch

text/plain

diff -dcrpN postgresql.orig/src/backend/access/common/tupdesc.c postgresql.4/src/backend/access/common/tupdesc.c
*** postgresql.orig/src/backend/access/common/tupdesc.c	2011-07-18 15:42:00.008379772 +0200
--- postgresql.4/src/backend/access/common/tupdesc.c	2011-08-02 11:49:16.452381480 +0200
*************** equalTupleDescs(TupleDesc tupdesc1, Tupl
*** 337,344 ****
  			return false;
  		if (attr1->atttypid != attr2->atttypid)
  			return false;
- 		if (attr1->attstattarget != attr2->attstattarget)
- 			return false;
  		if (attr1->attlen != attr2->attlen)
  			return false;
  		if (attr1->attndims != attr2->attndims)
--- 337,342 ----
*************** TupleDescInitEntry(TupleDesc desc,
*** 471,477 ****
  	else
  		MemSet(NameStr(att->attname), 0, NAMEDATALEN);
  
- 	att->attstattarget = -1;
  	att->attcacheoff = -1;
  	att->atttypmod = typmod;
  
--- 469,474 ----
diff -dcrpN postgresql.orig/src/backend/access/nbtree/nbtcompare.c postgresql.4/src/backend/access/nbtree/nbtcompare.c
*** postgresql.orig/src/backend/access/nbtree/nbtcompare.c	2011-01-04 15:13:15.816567224 +0100
--- postgresql.4/src/backend/access/nbtree/nbtcompare.c	2011-07-27 15:13:09.756534651 +0200
*************** btoidvectorcmp(PG_FUNCTION_ARGS)
*** 220,225 ****
--- 220,249 ----
  }
  
  Datum
+ btint2vectorcmp(PG_FUNCTION_ARGS)
+ {
+ 	int2vector  *a = (int2vector *) PG_GETARG_POINTER(0);
+ 	int2vector  *b = (int2vector *) PG_GETARG_POINTER(1);
+ 	int			i;
+ 
+ 	/* We arbitrarily choose to sort first by vector length */
+ 	if (a->dim1 != b->dim1)
+ 		PG_RETURN_INT32(a->dim1 - b->dim1);
+ 
+ 	for (i = 0; i < a->dim1; i++)
+ 	{
+ 		if (a->values[i] != b->values[i])
+ 		{
+ 			if (a->values[i] > b->values[i])
+ 				PG_RETURN_INT32(1);
+ 			else
+ 				PG_RETURN_INT32(-1);
+ 		}
+ 	}
+ 	PG_RETURN_INT32(0);
+ }
+ 
+ Datum
  btcharcmp(PG_FUNCTION_ARGS)
  {
  	char		a = PG_GETARG_CHAR(0);
diff -dcrpN postgresql.orig/src/backend/bootstrap/bootstrap.c postgresql.4/src/backend/bootstrap/bootstrap.c
*** postgresql.orig/src/backend/bootstrap/bootstrap.c	2011-07-18 15:42:00.015379264 +0200
--- postgresql.4/src/backend/bootstrap/bootstrap.c	2011-08-02 11:49:16.481379350 +0200
*************** DefineAttr(char *name, char *type, int a
*** 736,742 ****
  			attrtypes[attnum]->attndims = 0;
  	}
  
- 	attrtypes[attnum]->attstattarget = -1;
  	attrtypes[attnum]->attcacheoff = -1;
  	attrtypes[attnum]->atttypmod = -1;
  	attrtypes[attnum]->attislocal = true;
--- 736,741 ----
diff -dcrpN postgresql.orig/src/backend/catalog/heap.c postgresql.4/src/backend/catalog/heap.c
*** postgresql.orig/src/backend/catalog/heap.c	2011-07-24 18:16:45.258679387 +0200
--- postgresql.4/src/backend/catalog/heap.c	2011-08-02 11:59:09.568791478 +0200
*************** static List *insert_ordered_unique_oid(L
*** 132,168 ****
   */
  
  static FormData_pg_attribute a1 = {
! 	0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
  	SelfItemPointerAttributeNumber, 0, -1, -1,
  	false, 'p', 's', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a2 = {
! 	0, {"oid"}, OIDOID, 0, sizeof(Oid),
  	ObjectIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a3 = {
! 	0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
  	MinTransactionIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a4 = {
! 	0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
  	MinCommandIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a5 = {
! 	0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
  	MaxTransactionIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a6 = {
! 	0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
  	MaxCommandIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
--- 132,168 ----
   */
  
  static FormData_pg_attribute a1 = {
! 	0, {"ctid"}, TIDOID, sizeof(ItemPointerData),
  	SelfItemPointerAttributeNumber, 0, -1, -1,
  	false, 'p', 's', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a2 = {
! 	0, {"oid"}, OIDOID, sizeof(Oid),
  	ObjectIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a3 = {
! 	0, {"xmin"}, XIDOID, sizeof(TransactionId),
  	MinTransactionIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a4 = {
! 	0, {"cmin"}, CIDOID, sizeof(CommandId),
  	MinCommandIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a5 = {
! 	0, {"xmax"}, XIDOID, sizeof(TransactionId),
  	MaxTransactionIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
  
  static FormData_pg_attribute a6 = {
! 	0, {"cmax"}, CIDOID, sizeof(CommandId),
  	MaxCommandIdAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
*************** static FormData_pg_attribute a6 = {
*** 174,180 ****
   * used in SQL.
   */
  static FormData_pg_attribute a7 = {
! 	0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
  	TableOidAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
--- 174,180 ----
   * used in SQL.
   */
  static FormData_pg_attribute a7 = {
! 	0, {"tableoid"}, OIDOID, sizeof(Oid),
  	TableOidAttributeNumber, 0, -1, -1,
  	true, 'p', 'i', true, false, false, true, 0
  };
*************** InsertPgAttributeTuple(Relation pg_attri
*** 601,607 ****
  	values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_attribute->attrelid);
  	values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attribute->attname);
  	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
- 	values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attribute->attstattarget);
  	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
  	values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attribute->attnum);
  	values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attribute->attndims);
--- 601,606 ----
*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 672,683 ****
  		attr = tupdesc->attrs[i];
  		/* Fill in the correct relation OID */
  		attr->attrelid = new_rel_oid;
! 		/* Make sure these are OK, too */
! 		attr->attstattarget = -1;
  		attr->attcacheoff = -1;
  
  		InsertPgAttributeTuple(rel, attr, indstate);
  
  		/* Add dependency info */
  		myself.classId = RelationRelationId;
  		myself.objectId = new_rel_oid;
--- 671,683 ----
  		attr = tupdesc->attrs[i];
  		/* Fill in the correct relation OID */
  		attr->attrelid = new_rel_oid;
! 		/* Make sure this is OK, too */
  		attr->attcacheoff = -1;
  
  		InsertPgAttributeTuple(rel, attr, indstate);
  
+ 		AddStatistics(new_rel_oid, &attr->attnum, 1, (oidinhcount > 0), -1);
+ 
  		/* Add dependency info */
  		myself.classId = RelationRelationId;
  		myself.objectId = new_rel_oid;
*************** RemoveAttributeById(Oid relid, AttrNumbe
*** 1491,1499 ****
  		/* Remove any NOT NULL constraint the column may have */
  		attStruct->attnotnull = false;
  
- 		/* We don't want to keep stats for it anymore */
- 		attStruct->attstattarget = 0;
- 
  		/*
  		 * Change the column name to something that isn't likely to conflict
  		 */
--- 1491,1496 ----
*************** RemoveAttributeById(Oid relid, AttrNumbe
*** 1515,1522 ****
  
  	heap_close(attr_rel, RowExclusiveLock);
  
  	if (attnum > 0)
! 		RemoveStatistics(relid, attnum);
  
  	relation_close(rel, NoLock);
  }
--- 1512,1520 ----
  
  	heap_close(attr_rel, RowExclusiveLock);
  
+ 	/* Only drop pg_statistic entries for non system columns. */
  	if (attnum > 0)
! 		RemoveStatistics(relid, &attnum, 1);
  
  	relation_close(rel, NoLock);
  }
*************** heap_drop_with_catalog(Oid relid)
*** 1740,1746 ****
  	/*
  	 * delete statistics
  	 */
! 	RemoveStatistics(relid, 0);
  
  	/*
  	 * delete attribute tuples
--- 1738,1744 ----
  	/*
  	 * delete statistics
  	 */
! 	RemoveStatistics(relid, NULL, 0);
  
  	/*
  	 * delete attribute tuples
*************** cookConstraint(ParseState *pstate,
*** 2521,2539 ****
  
  
  /*
!  * RemoveStatistics --- remove entries in pg_statistic for a rel or column
   *
!  * If attnum is zero, remove all entries for rel; else remove only the one(s)
!  * for that column.
   */
  void
! RemoveStatistics(Oid relid, AttrNumber attnum)
  {
  	Relation	pgstatistic;
  	SysScanDesc scan;
  	ScanKeyData key[2];
  	int			nkeys;
  	HeapTuple	tuple;
  
  	pgstatistic = heap_open(StatisticRelationId, RowExclusiveLock);
  
--- 2519,2735 ----
  
  
  /*
!  * AddStatistics --- add an entry in pg_statistic
   *
!  * attnums		- an ordered array of AttrNumbers
!  * n_attnum		- number of elements in the array
!  * statistics_target	- the sampling size for this statistics
!  *
!  * Entries in pg_statistic are used by the planner to collect selectivity values.
!  * This function is called when a new relation is created or a new column is added
!  * to a relation. It is therefore ensured that every column has an entry during the
!  * lifetime of the relation since its creation. There is one exception from under
!  * this rule: thus function is a no-op during bootstrapping to avoid a catch-22
!  * situation where a pg_statistic entry would be created when pg_statistic itself
!  * doesn't exist yet. pg_statistic entries for system tables will be created by
!  * ANALYZE as before. The entry is created as invalid (stavalid == false) and
!  * the histogram columns are NULLs. This will also be fixed by ANALYZE.
   */
  void
! AddStatistics(Oid relid, AttrNumber *attnums, int n_attnums, bool inherited, int statistics_target)
! {
! 	Relation	rel;
! 	ScanKeyData	scanKey[2];
! 	SysScanDesc	scan;
! 	int2vector *attnumvector;
! 	HeapTuple	tuple;
! 	TupleDesc	tupDesc;
! 	Datum		values[Natts_pg_statistic];
! 	bool		nulls[Natts_pg_statistic];
! 	int		i, j;
! 
! 	if (IsBootstrapProcessingMode())
! 		return;
! 
! 	Assert(attnums != NULL);
! 	Assert(n_attnums > 0);
! 
! 	attnumvector = buildint2vector(attnums, n_attnums);
! 
! 	rel = heap_open(StatisticRelationId, RowExclusiveLock);
! 
! 	ScanKeyInit(&scanKey[0],
! 					Anum_pg_statistic_starelid,
! 					BTEqualStrategyNumber, F_OIDEQ,
! 					ObjectIdGetDatum(relid));
! 	ScanKeyInit(&scanKey[1],
! 					Anum_pg_statistic_staattnums,
! 					BTEqualStrategyNumber, F_ARRAY_EQ,
! 					PointerGetDatum(attnumvector));
! 
! 	scan = systable_beginscan(rel, StatisticRelidAttnumsInhIndexId, true,
! 									SnapshotNow, 2, scanKey);
! 
! 	tuple = systable_getnext(scan);
! 	if (HeapTupleIsValid(tuple))
! 	{
! 		systable_endscan(scan);
! 		elog(ERROR, "pg_statistic entry already exists for this table and set of columns");
! 	}
! 
! 	systable_endscan(scan);
! 
! 	for (i = 0; i < Natts_pg_statistic; i++)
! 		nulls[i] = true;
! 
! 	i = 0;
! 	values[i] = ObjectIdGetDatum(relid);		nulls[i++] = false;	/* starelid */
! 	values[i] = BoolGetDatum(inherited);		nulls[i++] = false;	/* stainherit */
! 	values[i] = BoolGetDatum(false);		nulls[i++] = false;	/* stavalid */
! 	values[i] = Int32GetDatum(statistics_target);	nulls[i++] = false;	/* statarget */
! 	values[i] = Float4GetDatum(0);			nulls[i++] = false;	/* stanullfrac */
! 	values[i] = Int32GetDatum(0);			nulls[i++] = false;	/* stawidth */
! 	values[i] = Float4GetDatum(0);			nulls[i++] = false;	/* stadistinct */
! 	for (j = 0; j < STATISTIC_NUM_SLOTS; j++)
! 	{
! 		values[i] = Int16GetDatum(0);		nulls[i++] = false;	/* stakindN */
! 	}
! 	for (j = 0; j < STATISTIC_NUM_SLOTS; j++)
! 	{
! 		values[i] = ObjectIdGetDatum(0);	nulls[i++] = false;	/* staopN */
! 	}
! 	values[i] = PointerGetDatum(attnumvector);	nulls[i++] = false;	/* stainherit */
! 
! 	tupDesc = RelationGetDescr(rel);
! 
! 	tuple = heap_form_tuple(tupDesc, values, nulls);
! 
! 	simple_heap_insert(rel, tuple);
! 
! 	CatalogUpdateIndexes(rel, tuple);
! 
! 	pfree(attnumvector);
! 
! 	relation_close(rel, RowExclusiveLock);
! }
! 
! typedef struct invalidate_stats {
! 	HeapTuple	tuple;
! 	struct invalidate_stats *next;
! } invalidate_stats;
! 
! /*
!  * InvalidateStatistics --- invalidate all pg_statistic entries of this attnum
!  *
!  * We need to collect copies of tuples that need invalidating in order not to
!  * conflict with the system table scan.
!  */
! void
! InvalidateStatistics(Oid relid, AttrNumber attnum)
! {
! 	Relation		rel;
! 	ScanKeyData		scanKey;
! 	SysScanDesc		scan;
! 	HeapTuple		tuple;
! 	TupleDesc		tupDesc;
! 	Form_pg_statistic	stattuple;
! 	invalidate_stats   *tupptr;
! 	invalidate_stats   *tupptr_next;
! 	int			i;
! 
! 	if (IsBootstrapProcessingMode())
! 		return;
! 
! 	Assert(attnums != NULL);
! 	Assert(n_attnums > 0);
! 
! 	rel = heap_open(StatisticRelationId, RowExclusiveLock);
! 	tupDesc = RelationGetDescr(rel);
! 
! 	ScanKeyInit(&scanKey,
! 					Anum_pg_statistic_starelid,
! 					BTEqualStrategyNumber, F_OIDEQ,
! 					ObjectIdGetDatum(relid));
! 
! 	scan = systable_beginscan(rel, StatisticRelidAttnumsInhIndexId, true,
! 									SnapshotNow, 1, &scanKey);
! 
! 
! 	tupptr = tupptr_next = NULL;
! 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
! 	{
! 		bool	isnull;
! 		Datum	attnvec;
! 		int2vector *attnumvector;
! 
! 		attnvec = heap_getattr(tuple, Anum_pg_statistic_staattnums, tupDesc, &isnull);
! 
! 		Assert(!isnull);
! 
! 		attnumvector = (int2vector *) DatumGetPointer(attnvec);
! 
! 		for (i = 0; i < attnumvector->dim1; i++)
! 		{
! 			if (attnumvector->values[i] == attnum)
! 			{
! 				invalidate_stats *tmp;
! 
! 
! 				tmp = palloc(sizeof(invalidate_stats));
! 				tmp->tuple = heap_copytuple(tuple);
! 				tmp->next = NULL;
! 
! 				if (tupptr == NULL)
! 					tupptr = tupptr_next = tmp;
! 				else
! 				{
! 					tupptr_next->next = tmp;
! 					tupptr_next = tmp;
! 				}
! 
! 				break; /* find next tuple */
! 			}
! 		}
! 	}
! 
! 	systable_endscan(scan);
! 
! 	while (tupptr)
! 	{
! 		stattuple = (Form_pg_statistic) GETSTRUCT(tupptr->tuple);
! 
! 		stattuple->stavalid = false;
! 
! 		simple_heap_update(rel, &tupptr->tuple->t_self, tupptr->tuple);
! 
! 		CatalogUpdateIndexes(rel, tupptr->tuple);
! 
! 		tupptr_next = tupptr->next;
! 
! 		heap_freetuple(tupptr->tuple);
! 		pfree(tupptr);
! 
! 		tupptr = tupptr_next;
! 	}
! 
! 	relation_close(rel, RowExclusiveLock);
! }
! 
! /*
!  * RemoveStatistics --- remove entries in pg_statistic for a rel's set of columns
!  *
!  * If attnums is NULL, remove all entries for rel; else remove only the one
!  * for that set of column(s).
!  */
! void
! RemoveStatistics(Oid relid, AttrNumber *attnums, int n_attnums)
  {
  	Relation	pgstatistic;
  	SysScanDesc scan;
  	ScanKeyData key[2];
  	int			nkeys;
  	HeapTuple	tuple;
+ 	int2vector *attnumvector = NULL;
  
  	pgstatistic = heap_open(StatisticRelationId, RowExclusiveLock);
  
*************** RemoveStatistics(Oid relid, AttrNumber a
*** 2542,2559 ****
  				BTEqualStrategyNumber, F_OIDEQ,
  				ObjectIdGetDatum(relid));
  
! 	if (attnum == 0)
  		nkeys = 1;
  	else
  	{
  		ScanKeyInit(&key[1],
! 					Anum_pg_statistic_staattnum,
! 					BTEqualStrategyNumber, F_INT2EQ,
! 					Int16GetDatum(attnum));
  		nkeys = 2;
  	}
  
! 	scan = systable_beginscan(pgstatistic, StatisticRelidAttnumInhIndexId, true,
  							  SnapshotNow, nkeys, key);
  
  	/* we must loop even when attnum != 0, in case of inherited stats */
--- 2738,2756 ----
  				BTEqualStrategyNumber, F_OIDEQ,
  				ObjectIdGetDatum(relid));
  
! 	if (attnums == NULL)
  		nkeys = 1;
  	else
  	{
+ 		attnumvector = buildint2vector(attnums, n_attnums);
  		ScanKeyInit(&key[1],
! 					Anum_pg_statistic_staattnums,
! 					BTEqualStrategyNumber, F_INT2VECTOREQ,
! 					PointerGetDatum(attnumvector));
  		nkeys = 2;
  	}
  
! 	scan = systable_beginscan(pgstatistic, StatisticRelidAttnumsInhIndexId, true,
  							  SnapshotNow, nkeys, key);
  
  	/* we must loop even when attnum != 0, in case of inherited stats */
*************** RemoveStatistics(Oid relid, AttrNumber a
*** 2562,2567 ****
--- 2759,2767 ----
  
  	systable_endscan(scan);
  
+ 	if (attnumvector)
+ 		pfree(attnumvector);
+ 
  	heap_close(pgstatistic, RowExclusiveLock);
  }
  
diff -dcrpN postgresql.orig/src/backend/catalog/index.c postgresql.4/src/backend/catalog/index.c
*** postgresql.orig/src/backend/catalog/index.c	2011-07-24 18:16:45.259679336 +0200
--- postgresql.4/src/backend/catalog/index.c	2011-08-02 11:49:16.512377071 +0200
*************** ConstructTupleDescriptor(Relation heapRe
*** 347,353 ****
  			 */
  			to->attnum = i + 1;
  
- 			to->attstattarget = -1;
  			to->attcacheoff = -1;
  			to->attnotnull = false;
  			to->atthasdef = false;
--- 347,352 ----
*************** ConstructTupleDescriptor(Relation heapRe
*** 385,391 ****
  			to->attbyval = typeTup->typbyval;
  			to->attstorage = typeTup->typstorage;
  			to->attalign = typeTup->typalign;
- 			to->attstattarget = -1;
  			to->attcacheoff = -1;
  			to->atttypmod = -1;
  			to->attislocal = true;
--- 384,389 ----
*************** index_drop(Oid indexId)
*** 1357,1363 ****
  	 * them.
  	 */
  	if (hasexprs)
! 		RemoveStatistics(indexId, 0);
  
  	/*
  	 * fix ATTRIBUTE relation
--- 1355,1361 ----
  	 * them.
  	 */
  	if (hasexprs)
! 		RemoveStatistics(indexId, NULL, 0);
  
  	/*
  	 * fix ATTRIBUTE relation
diff -dcrpN postgresql.orig/src/backend/catalog/system_views.sql postgresql.4/src/backend/catalog/system_views.sql
*** postgresql.orig/src/backend/catalog/system_views.sql	2011-07-24 18:16:45.262679185 +0200
--- postgresql.4/src/backend/catalog/system_views.sql	2011-07-27 15:33:44.392554069 +0200
*************** CREATE VIEW pg_stats AS
*** 141,147 ****
              WHEN stakind4 = 3 THEN stanumbers4[1]
          END AS correlation
      FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid)
!          JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum)
           LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
      WHERE NOT attisdropped AND has_column_privilege(c.oid, a.attnum, 'select');
  
--- 141,147 ----
              WHEN stakind4 = 3 THEN stanumbers4[1]
          END AS correlation
      FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid)
!          JOIN pg_attribute a ON (c.oid = attrelid AND array_length(s.staattnums, 1) = 1 AND attnum = s.staattnums[0])
           LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
      WHERE NOT attisdropped AND has_column_privilege(c.oid, a.attnum, 'select');
  
diff -dcrpN postgresql.orig/src/backend/commands/analyze.c postgresql.4/src/backend/commands/analyze.c
*** postgresql.orig/src/backend/commands/analyze.c	2011-06-20 10:11:35.729661282 +0200
--- postgresql.4/src/backend/commands/analyze.c	2011-08-02 11:51:06.071322632 +0200
***************
*** 42,47 ****
--- 42,48 ----
  #include "storage/procarray.h"
  #include "utils/acl.h"
  #include "utils/attoptcache.h"
+ #include "utils/builtins.h"
  #include "utils/datum.h"
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
*************** static void compute_index_stats(Relation
*** 93,99 ****
  					AnlIndexData *indexdata, int nindexes,
  					HeapTuple *rows, int numrows,
  					MemoryContext col_context);
! static VacAttrStats *examine_attribute(Relation onerel, int attnum,
  				  Node *index_expr);
  static int acquire_sample_rows(Relation onerel, HeapTuple *rows,
  					int targrows, double *totalrows, double *totaldeadrows);
--- 94,100 ----
  					AnlIndexData *indexdata, int nindexes,
  					HeapTuple *rows, int numrows,
  					MemoryContext col_context);
! static VacAttrStats *examine_attribute(Relation onerel, AttrNumber attnum,
  				  Node *index_expr);
  static int acquire_sample_rows(Relation onerel, HeapTuple *rows,
  					int targrows, double *totalrows, double *totaldeadrows);
*************** compute_index_stats(Relation onerel, dou
*** 792,797 ****
--- 793,827 ----
  }
  
  /*
+  * statistics_target -- returns pg_statistic.statarget
+  */
+ static int4
+ statistics_target(Oid relid, AttrNumber *attnums, int n_attnums, bool inherited)
+ {
+ 	int2vector	   *attnumvector;
+ 	HeapTuple		tuple;
+ 	int4			statarget = -1; /* default */
+ 
+ 	attnumvector = buildint2vector(attnums, n_attnums);
+ 	tuple = SearchSysCache3(STATRELATTINH,
+ 								ObjectIdGetDatum(relid),
+ 								PointerGetDatum(attnumvector),
+ 								BoolGetDatum(inherited));
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		Form_pg_statistic stats = (Form_pg_statistic) GETSTRUCT(tuple);
+ 
+ 		if (stats->stavalid)
+ 			statarget = stats->statarget;
+ 
+ 		ReleaseSysCache(tuple);
+ 	}
+ 
+ 	return statarget;
+ }
+ 
+ 
+ /*
   * examine_attribute -- pre-analysis of a single column
   *
   * Determine whether the column is analyzable; if so, create and initialize
*************** compute_index_stats(Relation onerel, dou
*** 801,810 ****
   * and index_expr is the expression tree representing the column's data.
   */
  static VacAttrStats *
! examine_attribute(Relation onerel, int attnum, Node *index_expr)
  {
  	Form_pg_attribute attr = onerel->rd_att->attrs[attnum - 1];
  	HeapTuple	typtuple;
  	VacAttrStats *stats;
  	int			i;
  	bool		ok;
--- 831,841 ----
   * and index_expr is the expression tree representing the column's data.
   */
  static VacAttrStats *
! examine_attribute(Relation onerel, AttrNumber attnum, Node *index_expr)
  {
  	Form_pg_attribute attr = onerel->rd_att->attrs[attnum - 1];
  	HeapTuple	typtuple;
+ 	int4		statarget;
  	VacAttrStats *stats;
  	int			i;
  	bool		ok;
*************** examine_attribute(Relation onerel, int a
*** 814,820 ****
  		return NULL;
  
  	/* Don't analyze column if user has specified not to */
! 	if (attr->attstattarget == 0)
  		return NULL;
  
  	/*
--- 845,852 ----
  		return NULL;
  
  	/* Don't analyze column if user has specified not to */
! 	statarget = statistics_target(onerel->rd_id, &attnum, 1, onerel->rd_att->attrs[attnum - 1]->attinhcount > 0);
! 	if (statarget == 0)
  		return NULL;
  
  	/*
*************** examine_attribute(Relation onerel, int a
*** 823,828 ****
--- 855,861 ----
  	 */
  	stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats));
  	stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE);
+ 	stats->statarget = stats->oldtarget = statarget;
  	memcpy(stats->attr, attr, ATTRIBUTE_FIXED_PART_SIZE);
  
  	/*
*************** update_attstats(Oid relid, bool inh, int
*** 1573,1578 ****
--- 1606,1612 ----
  		int			i,
  					k,
  					n;
+ 		int2vector *attnumvector = NULL;
  		Datum		values[Natts_pg_statistic];
  		bool		nulls[Natts_pg_statistic];
  		bool		replaces[Natts_pg_statistic];
*************** update_attstats(Oid relid, bool inh, int
*** 1591,1598 ****
  		}
  
  		values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(relid);
- 		values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(stats->attr->attnum);
  		values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inh);
  		values[Anum_pg_statistic_stanullfrac - 1] = Float4GetDatum(stats->stanullfrac);
  		values[Anum_pg_statistic_stawidth - 1] = Int32GetDatum(stats->stawidth);
  		values[Anum_pg_statistic_stadistinct - 1] = Float4GetDatum(stats->stadistinct);
--- 1625,1633 ----
  		}
  
  		values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(relid);
  		values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inh);
+ 		values[Anum_pg_statistic_stavalid] = BoolGetDatum(true);
+ 		values[Anum_pg_statistic_statarget] = Int32GetDatum(stats->oldtarget);
  		values[Anum_pg_statistic_stanullfrac - 1] = Float4GetDatum(stats->stanullfrac);
  		values[Anum_pg_statistic_stawidth - 1] = Int32GetDatum(stats->stawidth);
  		values[Anum_pg_statistic_stadistinct - 1] = Float4GetDatum(stats->stadistinct);
*************** update_attstats(Oid relid, bool inh, int
*** 1606,1611 ****
--- 1641,1650 ----
  		{
  			values[i++] = ObjectIdGetDatum(stats->staop[k]);	/* staopN */
  		}
+ 
+ 		attnumvector = buildint2vector(&(stats->attr->attnum), 1);
+ 		values[Anum_pg_statistic_staattnums - 1] = PointerGetDatum(attnumvector);	/* staattnums */
+ 
  		i = Anum_pg_statistic_stanumbers1 - 1;
  		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
  		{
*************** update_attstats(Oid relid, bool inh, int
*** 1655,1661 ****
  		/* Is there already a pg_statistic tuple for this attribute? */
  		oldtup = SearchSysCache3(STATRELATTINH,
  								 ObjectIdGetDatum(relid),
! 								 Int16GetDatum(stats->attr->attnum),
  								 BoolGetDatum(inh));
  
  		if (HeapTupleIsValid(oldtup))
--- 1694,1700 ----
  		/* Is there already a pg_statistic tuple for this attribute? */
  		oldtup = SearchSysCache3(STATRELATTINH,
  								 ObjectIdGetDatum(relid),
! 								 PointerGetDatum(attnumvector),
  								 BoolGetDatum(inh));
  
  		if (HeapTupleIsValid(oldtup))
*************** update_attstats(Oid relid, bool inh, int
*** 1676,1681 ****
--- 1715,1722 ----
  			simple_heap_insert(sd, stup);
  		}
  
+ 		pfree(attnumvector);
+ 
  		/* update indexes too */
  		CatalogUpdateIndexes(sd, stup);
  
*************** static int	compare_mcvs(const void *a, c
*** 1791,1805 ****
  static bool
  std_typanalyze(VacAttrStats *stats)
  {
- 	Form_pg_attribute attr = stats->attr;
  	Oid			ltopr;
  	Oid			eqopr;
  	StdAnalyzeData *mystats;
  
! 	/* If the attstattarget column is negative, use the default value */
  	/* NB: it is okay to scribble on stats->attr since it's a copy */
! 	if (attr->attstattarget < 0)
! 		attr->attstattarget = default_statistics_target;
  
  	/* Look for default "<" and "=" operators for column's type */
  	get_sort_group_operators(stats->attrtypid,
--- 1832,1845 ----
  static bool
  std_typanalyze(VacAttrStats *stats)
  {
  	Oid			ltopr;
  	Oid			eqopr;
  	StdAnalyzeData *mystats;
  
! 	/* If the statarget column is negative, use the default value */
  	/* NB: it is okay to scribble on stats->attr since it's a copy */
! 	if (stats->statarget < 0)
! 		stats->statarget = default_statistics_target;
  
  	/* Look for default "<" and "=" operators for column's type */
  	get_sort_group_operators(stats->attrtypid,
*************** std_typanalyze(VacAttrStats *stats)
*** 1844,1857 ****
  		 * know it at this point.
  		 *--------------------
  		 */
! 		stats->minrows = 300 * attr->attstattarget;
  	}
  	else
  	{
  		/* Can't do much but the minimal stuff */
  		stats->compute_stats = compute_minimal_stats;
  		/* Might as well use the same minrows as above */
! 		stats->minrows = 300 * attr->attstattarget;
  	}
  
  	return true;
--- 1884,1897 ----
  		 * know it at this point.
  		 *--------------------
  		 */
! 		stats->minrows = 300 * stats->statarget;
  	}
  	else
  	{
  		/* Can't do much but the minimal stuff */
  		stats->compute_stats = compute_minimal_stats;
  		/* Might as well use the same minrows as above */
! 		stats->minrows = 300 * stats->statarget;
  	}
  
  	return true;
*************** compute_minimal_stats(VacAttrStatsP stat
*** 1896,1902 ****
  	TrackItem  *track;
  	int			track_cnt,
  				track_max;
! 	int			num_mcv = stats->attr->attstattarget;
  	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
  
  	/*
--- 1936,1942 ----
  	TrackItem  *track;
  	int			track_cnt,
  				track_max;
! 	int			num_mcv = stats->statarget;
  	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
  
  	/*
*************** compute_scalar_stats(VacAttrStatsP stats
*** 2223,2230 ****
  	int		   *tupnoLink;
  	ScalarMCVItem *track;
  	int			track_cnt = 0;
! 	int			num_mcv = stats->attr->attstattarget;
! 	int			num_bins = stats->attr->attstattarget;
  	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
  
  	values = (ScalarItem *) palloc(samplerows * sizeof(ScalarItem));
--- 2263,2270 ----
  	int		   *tupnoLink;
  	ScalarMCVItem *track;
  	int			track_cnt = 0;
! 	int			num_mcv = stats->statarget;
! 	int			num_bins = stats->statarget;
  	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
  
  	values = (ScalarItem *) palloc(samplerows * sizeof(ScalarItem));
diff -dcrpN postgresql.orig/src/backend/commands/tablecmds.c postgresql.4/src/backend/commands/tablecmds.c
*** postgresql.orig/src/backend/commands/tablecmds.c	2011-07-24 18:16:45.267678934 +0200
--- postgresql.4/src/backend/commands/tablecmds.c	2011-08-02 11:49:16.568372952 +0200
***************
*** 35,40 ****
--- 35,41 ----
  #include "catalog/pg_inherits_fn.h"
  #include "catalog/pg_namespace.h"
  #include "catalog/pg_opclass.h"
+ #include "catalog/pg_statistic.h"
  #include "catalog/pg_tablespace.h"
  #include "catalog/pg_trigger.h"
  #include "catalog/pg_type.h"
*************** ATExecAddColumn(List **wqueue, AlteredTa
*** 4319,4325 ****
  	attribute.attrelid = myrelid;
  	namestrcpy(&(attribute.attname), colDef->colname);
  	attribute.atttypid = typeOid;
- 	attribute.attstattarget = (newattnum > 0) ? -1 : 0;
  	attribute.attlen = tform->typlen;
  	attribute.attcacheoff = -1;
  	attribute.atttypmod = typmod;
--- 4320,4325 ----
*************** ATExecAddColumn(List **wqueue, AlteredTa
*** 4481,4486 ****
--- 4481,4488 ----
  	add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
  	add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
  
+ 	AddStatistics(myrelid, &attribute.attnum, 1, attribute.attinhcount, (newattnum > 0) ? -1 : 0);
+ 
  	/*
  	 * Propagate to children as appropriate.  Unlike most other ALTER
  	 * routines, we have to do this one level of recursion at a time; we can't
*************** ATExecSetStatistics(Relation rel, const 
*** 4817,4824 ****
--- 4819,4832 ----
  {
  	int			newtarget;
  	Relation	attrelation;
+ 	Relation	statsrelation;
+ 	Oid		relid;
  	HeapTuple	tuple;
  	Form_pg_attribute attrtuple;
+ 	AttrNumber	attnum;
+ 	bool		inherited;
+ 	int2vector *attnumvector;
+ 	Form_pg_statistic stattuple;
  
  	Assert(IsA(newValue, Integer));
  	newtarget = intVal(newValue);
*************** ATExecSetStatistics(Relation rel, const 
*** 4844,4850 ****
  
  	attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
  
! 	tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
  
  	if (!HeapTupleIsValid(tuple))
  		ereport(ERROR,
--- 4852,4860 ----
  
  	attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
  
! 	relid = RelationGetRelid(rel);
! 
! 	tuple = SearchSysCacheAttName(relid, colName);
  
  	if (!HeapTupleIsValid(tuple))
  		ereport(ERROR,
*************** ATExecSetStatistics(Relation rel, const 
*** 4859,4874 ****
  				 errmsg("cannot alter system column \"%s\"",
  						colName)));
  
! 	attrtuple->attstattarget = newtarget;
  
! 	simple_heap_update(attrelation, &tuple->t_self, tuple);
  
! 	/* keep system catalog indexes current */
! 	CatalogUpdateIndexes(attrelation, tuple);
  
! 	heap_freetuple(tuple);
  
! 	heap_close(attrelation, RowExclusiveLock);
  }
  
  static void
--- 4869,4909 ----
  				 errmsg("cannot alter system column \"%s\"",
  						colName)));
  
! 	attnum = attrtuple->attnum;
! 	inherited = (attrtuple->attinhcount > 0);
  
! 	ReleaseSysCache(tuple);
  
! 	heap_close(attrelation, RowExclusiveLock);
  
! 	statsrelation = heap_open(StatisticRelationId, RowExclusiveLock);
  
! 	attnumvector = buildint2vector(&attnum, 1);
! 
! 	tuple = SearchSysCacheCopy3(STATRELATTINH,
! 								ObjectIdGetDatum(relid),
! 								PointerGetDatum(attnumvector),
! 								BoolGetDatum(inherited));
! 
! 	pfree(attnumvector);
! 
! 	if (!HeapTupleIsValid(tuple))
! 		AddStatistics(relid, &attnum, 1, inherited, newtarget);
! 	else
! 	{
! 		stattuple = (Form_pg_statistic) GETSTRUCT(tuple);
! 
! 		stattuple->statarget = newtarget;
! 
! 		simple_heap_update(statsrelation, &tuple->t_self, tuple);
! 
! 		/* keep system catalog indexes current */
! 		CatalogUpdateIndexes(statsrelation, tuple);
! 
! 		heap_freetuple(tuple);
! 	}
! 
! 	heap_close(statsrelation, RowExclusiveLock);
  }
  
  static void
*************** ATExecAlterColumnType(AlteredTableInfo *
*** 7368,7376 ****
  	add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
  
  	/*
! 	 * Drop any pg_statistic entry for the column, since it's now wrong type
  	 */
! 	RemoveStatistics(RelationGetRelid(rel), attnum);
  
  	/*
  	 * Update the default, if present, by brute force --- remove and re-add
--- 7403,7411 ----
  	add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
  
  	/*
! 	 * Invalidate any pg_statistic entry for the column, since it's now wrong type
  	 */
! 	InvalidateStatistics(RelationGetRelid(rel), attnum);
  
  	/*
  	 * Update the default, if present, by brute force --- remove and re-add
diff -dcrpN postgresql.orig/src/backend/executor/nodeHash.c postgresql.4/src/backend/executor/nodeHash.c
*** postgresql.orig/src/backend/executor/nodeHash.c	2011-04-11 15:36:27.096816773 +0200
--- postgresql.4/src/backend/executor/nodeHash.c	2011-08-02 11:49:16.588371482 +0200
***************
*** 33,38 ****
--- 33,39 ----
  #include "executor/nodeHashjoin.h"
  #include "miscadmin.h"
  #include "parser/parse_expr.h"
+ #include "utils/builtins.h"
  #include "utils/dynahash.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
*************** ExecHashBuildSkewHash(HashJoinTable hash
*** 1126,1131 ****
--- 1127,1133 ----
  	int			nvalues;
  	float4	   *numbers;
  	int			nnumbers;
+ 	int2vector *attnumvector;
  
  	/* Do nothing if planner didn't identify the outer relation's join key */
  	if (!OidIsValid(node->skewTable))
*************** ExecHashBuildSkewHash(HashJoinTable hash
*** 1137,1146 ****
  	/*
  	 * Try to find the MCV statistics for the outer relation's join key.
  	 */
  	statsTuple = SearchSysCache3(STATRELATTINH,
  								 ObjectIdGetDatum(node->skewTable),
! 								 Int16GetDatum(node->skewColumn),
  								 BoolGetDatum(node->skewInherit));
  	if (!HeapTupleIsValid(statsTuple))
  		return;
  
--- 1139,1161 ----
  	/*
  	 * Try to find the MCV statistics for the outer relation's join key.
  	 */
+ 	attnumvector = buildint2vector(&(node->skewColumn), 1);
  	statsTuple = SearchSysCache3(STATRELATTINH,
  								 ObjectIdGetDatum(node->skewTable),
! 								 PointerGetDatum(attnumvector),
  								 BoolGetDatum(node->skewInherit));
+ 	pfree(attnumvector);
+ 
+ 	/* check whether the stats entry is valid */
+ 	if (HeapTupleIsValid(statsTuple))
+ 	{
+ 		if (!((Form_pg_statistic)GETSTRUCT(statsTuple))->stavalid)
+ 		{
+ 			ReleaseSysCache(statsTuple);
+ 			statsTuple = NULL;
+ 		}
+ 	}
+ 
  	if (!HeapTupleIsValid(statsTuple))
  		return;
  
diff -dcrpN postgresql.orig/src/backend/tsearch/ts_typanalyze.c postgresql.4/src/backend/tsearch/ts_typanalyze.c
*** postgresql.orig/src/backend/tsearch/ts_typanalyze.c	2011-01-04 15:13:16.013557090 +0100
--- postgresql.4/src/backend/tsearch/ts_typanalyze.c	2011-08-02 11:49:16.626368688 +0200
*************** Datum
*** 55,70 ****
  ts_typanalyze(PG_FUNCTION_ARGS)
  {
  	VacAttrStats *stats = (VacAttrStats *) PG_GETARG_POINTER(0);
- 	Form_pg_attribute attr = stats->attr;
  
! 	/* If the attstattarget column is negative, use the default value */
! 	/* NB: it is okay to scribble on stats->attr since it's a copy */
! 	if (attr->attstattarget < 0)
! 		attr->attstattarget = default_statistics_target;
  
  	stats->compute_stats = compute_tsvector_stats;
  	/* see comment about the choice of minrows in commands/analyze.c */
! 	stats->minrows = 300 * attr->attstattarget;
  
  	PG_RETURN_BOOL(true);
  }
--- 55,69 ----
  ts_typanalyze(PG_FUNCTION_ARGS)
  {
  	VacAttrStats *stats = (VacAttrStats *) PG_GETARG_POINTER(0);
  
! 	/* If the statarget column is negative, use the default value */
! 	/* NB: it is okay to scribble on stats->statarget since it's a copy */
! 	if (stats->statarget < 0)
! 		stats->statarget = default_statistics_target;
  
  	stats->compute_stats = compute_tsvector_stats;
  	/* see comment about the choice of minrows in commands/analyze.c */
! 	stats->minrows = 300 * stats->statarget;
  
  	PG_RETURN_BOOL(true);
  }
*************** compute_tsvector_stats(VacAttrStats *sta
*** 167,173 ****
  	 * the number of individual lexeme values tracked in pg_statistic ought to
  	 * be more than the number of values for a simple scalar column.
  	 */
! 	num_mcelem = stats->attr->attstattarget * 10;
  
  	/*
  	 * We set bucket width equal to (num_mcelem + 10) / 0.007 as per the
--- 166,172 ----
  	 * the number of individual lexeme values tracked in pg_statistic ought to
  	 * be more than the number of values for a simple scalar column.
  	 */
! 	num_mcelem = stats->statarget * 10;
  
  	/*
  	 * We set bucket width equal to (num_mcelem + 10) / 0.007 as per the
diff -dcrpN postgresql.orig/src/backend/utils/adt/int.c postgresql.4/src/backend/utils/adt/int.c
*** postgresql.orig/src/backend/utils/adt/int.c	2011-06-20 10:11:35.739660477 +0200
--- postgresql.4/src/backend/utils/adt/int.c	2011-07-27 15:13:09.758534476 +0200
*************** int2vectorsend(PG_FUNCTION_ARGS)
*** 254,274 ****
  	return array_send(fcinfo);
  }
  
- /*
-  * We don't have a complete set of int2vector support routines,
-  * but we need int2vectoreq for catcache indexing.
-  */
  Datum
  int2vectoreq(PG_FUNCTION_ARGS)
  {
! 	int2vector *a = (int2vector *) PG_GETARG_POINTER(0);
! 	int2vector *b = (int2vector *) PG_GETARG_POINTER(1);
  
! 	if (a->dim1 != b->dim1)
! 		PG_RETURN_BOOL(false);
! 	PG_RETURN_BOOL(memcmp(a->values, b->values, a->dim1 * sizeof(int2)) == 0);
  }
  
  
  /*****************************************************************************
   *	 PUBLIC ROUTINES														 *
--- 254,306 ----
  	return array_send(fcinfo);
  }
  
  Datum
  int2vectoreq(PG_FUNCTION_ARGS)
  {
! 	int32		cmp = DatumGetInt32(btint2vectorcmp(fcinfo));
  
! 	PG_RETURN_BOOL(cmp == 0);
! }
! 
! Datum
! int2vectorne(PG_FUNCTION_ARGS)
! {
! 	int32		cmp = DatumGetInt32(btint2vectorcmp(fcinfo));
! 
! 	PG_RETURN_BOOL(cmp != 0);
! }
! 
! Datum
! int2vectorlt(PG_FUNCTION_ARGS)
! {
! 	int32		cmp = DatumGetInt32(btint2vectorcmp(fcinfo));
! 
! 	PG_RETURN_BOOL(cmp < 0);
  }
  
+ Datum
+ int2vectorle(PG_FUNCTION_ARGS)
+ {
+ 	int32		cmp = DatumGetInt32(btint2vectorcmp(fcinfo));
+ 
+ 	PG_RETURN_BOOL(cmp <= 0);
+ }
+ 
+ Datum
+ int2vectorge(PG_FUNCTION_ARGS)
+ {
+ 	int32		cmp = DatumGetInt32(btint2vectorcmp(fcinfo));
+ 
+ 	PG_RETURN_BOOL(cmp >= 0);
+ }
+ 
+ Datum
+ int2vectorgt(PG_FUNCTION_ARGS)
+ {
+ 	int32		cmp = DatumGetInt32(btint2vectorcmp(fcinfo));
+ 
+ 	PG_RETURN_BOOL(cmp > 0);
+ }
  
  /*****************************************************************************
   *	 PUBLIC ROUTINES														 *
diff -dcrpN postgresql.orig/src/backend/utils/adt/selfuncs.c postgresql.4/src/backend/utils/adt/selfuncs.c
*** postgresql.orig/src/backend/utils/adt/selfuncs.c	2011-07-18 15:42:00.064375706 +0200
--- postgresql.4/src/backend/utils/adt/selfuncs.c	2011-08-02 11:49:16.651366852 +0200
*************** get_join_variables(PlannerInfo *root, Li
*** 4072,4077 ****
--- 4072,4105 ----
  }
  
  /*
+  * validate_statistics -- sets vardata->statsTuple only if the statistics is valid
+  */
+ void
+ validate_statistics(VariableStatData *vardata,
+ 			Oid relid, AttrNumber *attnums, int n_attnums, bool inherited)
+ {
+ 	int2vector *attnumvector = buildint2vector(attnums, n_attnums);
+ 	HeapTuple	tuple;
+ 
+ 	tuple = SearchSysCache3(STATRELATTINH,
+ 									ObjectIdGetDatum(relid),
+ 									PointerGetDatum(attnumvector),
+ 									  BoolGetDatum(inherited));
+ 	pfree(attnumvector);
+ 
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		if (((Form_pg_statistic) GETSTRUCT(tuple))->stavalid)
+ 		{
+ 			vardata->statsTuple = tuple;
+ 			vardata->freefunc = ReleaseSysCache;
+ 		}
+ 		else
+ 			ReleaseSysCache(tuple);
+ 	}
+ }
+ 
+ /*
   * examine_variable
   *		Try to look up statistical data about an expression.
   *		Fill in a VariableStatData struct to describe the expression.
*************** examine_variable(PlannerInfo *root, Node
*** 4150,4160 ****
  		}
  		else if (rte->rtekind == RTE_RELATION)
  		{
! 			vardata->statsTuple = SearchSysCache3(STATRELATTINH,
! 												ObjectIdGetDatum(rte->relid),
! 												Int16GetDatum(var->varattno),
! 												  BoolGetDatum(rte->inh));
! 			vardata->freefunc = ReleaseSysCache;
  		}
  		else
  		{
--- 4178,4184 ----
  		}
  		else if (rte->rtekind == RTE_RELATION)
  		{
! 			validate_statistics(vardata, rte->relid, &(var->varattno), 1, rte->inh);
  		}
  		else
  		{
*************** examine_variable(PlannerInfo *root, Node
*** 4289,4300 ****
  						}
  						else if (index->indpred == NIL)
  						{
! 							vardata->statsTuple =
! 								SearchSysCache3(STATRELATTINH,
! 										   ObjectIdGetDatum(index->indexoid),
! 												Int16GetDatum(pos + 1),
! 												BoolGetDatum(false));
! 							vardata->freefunc = ReleaseSysCache;
  						}
  						if (vardata->statsTuple)
  							break;
--- 4313,4321 ----
  						}
  						else if (index->indpred == NIL)
  						{
! 							int2		attnum = pos + 1;
! 
! 							validate_statistics(vardata, index->indexoid, &attnum, 1, false);
  						}
  						if (vardata->statsTuple)
  							break;
*************** btcostestimate(PG_FUNCTION_ARGS)
*** 6257,6269 ****
  				elog(ERROR, "no function provided to release variable stats with");
  		}
  		else
! 		{
! 			vardata.statsTuple = SearchSysCache3(STATRELATTINH,
! 												 ObjectIdGetDatum(relid),
! 												 Int16GetDatum(colnum),
! 												 BoolGetDatum(rte->inh));
! 			vardata.freefunc = ReleaseSysCache;
! 		}
  	}
  	else
  	{
--- 6278,6284 ----
  				elog(ERROR, "no function provided to release variable stats with");
  		}
  		else
! 			validate_statistics(&vardata, relid, &colnum, 1, rte->inh);
  	}
  	else
  	{
*************** btcostestimate(PG_FUNCTION_ARGS)
*** 6283,6295 ****
  				elog(ERROR, "no function provided to release variable stats with");
  		}
  		else
! 		{
! 			vardata.statsTuple = SearchSysCache3(STATRELATTINH,
! 												 ObjectIdGetDatum(relid),
! 												 Int16GetDatum(colnum),
! 												 BoolGetDatum(false));
! 			vardata.freefunc = ReleaseSysCache;
! 		}
  	}
  
  	if (HeapTupleIsValid(vardata.statsTuple))
--- 6298,6304 ----
  				elog(ERROR, "no function provided to release variable stats with");
  		}
  		else
! 			validate_statistics(&vardata, relid, &colnum, 1, false);
  	}
  
  	if (HeapTupleIsValid(vardata.statsTuple))
diff -dcrpN postgresql.orig/src/backend/utils/cache/lsyscache.c postgresql.4/src/backend/utils/cache/lsyscache.c
*** postgresql.orig/src/backend/utils/cache/lsyscache.c	2011-07-18 15:42:00.066375563 +0200
--- postgresql.4/src/backend/utils/cache/lsyscache.c	2011-08-02 11:49:16.684364424 +0200
*************** get_attavgwidth(Oid relid, AttrNumber at
*** 2632,2637 ****
--- 2632,2638 ----
  {
  	HeapTuple	tp;
  	int32		stawidth;
+ 	int2vector *attnumvector = NULL;
  
  	if (get_attavgwidth_hook)
  	{
*************** get_attavgwidth(Oid relid, AttrNumber at
*** 2639,2648 ****
  		if (stawidth > 0)
  			return stawidth;
  	}
  	tp = SearchSysCache3(STATRELATTINH,
  						 ObjectIdGetDatum(relid),
! 						 Int16GetDatum(attnum),
  						 BoolGetDatum(false));
  	if (HeapTupleIsValid(tp))
  	{
  		stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
--- 2640,2651 ----
  		if (stawidth > 0)
  			return stawidth;
  	}
+ 	attnumvector = buildint2vector(&attnum, 1);
  	tp = SearchSysCache3(STATRELATTINH,
  						 ObjectIdGetDatum(relid),
! 						 PointerGetDatum(attnumvector),
  						 BoolGetDatum(false));
+ 	pfree(attnumvector);
  	if (HeapTupleIsValid(tp))
  	{
  		stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
*************** get_attstatsslot(HeapTuple statstuple,
*** 2721,2728 ****
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stavalues1 + i,
  							  &isnull);
  		if (isnull)
! 			elog(ERROR, "stavalues is null");
  		statarray = DatumGetArrayTypeP(val);
  
  		/*
--- 2724,2732 ----
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stavalues1 + i,
  							  &isnull);
+ 		/* invalid stats record, i.e. analyze hasn't yet run for this column */
  		if (isnull)
! 			return false;
  		statarray = DatumGetArrayTypeP(val);
  
  		/*
*************** get_attstatsslot(HeapTuple statstuple,
*** 2775,2782 ****
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stanumbers1 + i,
  							  &isnull);
  		if (isnull)
! 			elog(ERROR, "stanumbers is null");
  		statarray = DatumGetArrayTypeP(val);
  
  		/*
--- 2779,2787 ----
  		val = SysCacheGetAttr(STATRELATTINH, statstuple,
  							  Anum_pg_statistic_stanumbers1 + i,
  							  &isnull);
+ 		/* invalid stats record, i.e. analyze hasn't yet run for this column */
  		if (isnull)
! 			return false;
  		statarray = DatumGetArrayTypeP(val);
  
  		/*
diff -dcrpN postgresql.orig/src/backend/utils/cache/syscache.c postgresql.4/src/backend/utils/cache/syscache.c
*** postgresql.orig/src/backend/utils/cache/syscache.c	2011-06-20 10:11:35.741660316 +0200
--- postgresql.4/src/backend/utils/cache/syscache.c	2011-08-02 14:05:12.866629811 +0200
*************** static const struct cachedesc cacheinfo[
*** 588,598 ****
  		1024
  	},
  	{StatisticRelationId,		/* STATRELATTINH */
! 		StatisticRelidAttnumInhIndexId,
  		3,
  		{
  			Anum_pg_statistic_starelid,
! 			Anum_pg_statistic_staattnum,
  			Anum_pg_statistic_stainherit,
  			0
  		},
--- 588,598 ----
  		1024
  	},
  	{StatisticRelationId,		/* STATRELATTINH */
! 		StatisticRelidAttnumsInhIndexId,
  		3,
  		{
  			Anum_pg_statistic_starelid,
! 			Anum_pg_statistic_staattnums,
  			Anum_pg_statistic_stainherit,
  			0
  		},
diff -dcrpN postgresql.orig/src/include/catalog/heap.h postgresql.4/src/include/catalog/heap.h
*** postgresql.orig/src/include/catalog/heap.h	2011-07-24 18:16:45.286677978 +0200
--- postgresql.4/src/include/catalog/heap.h	2011-08-02 11:49:16.715362145 +0200
*************** extern void RemoveAttributeById(Oid reli
*** 107,113 ****
  extern void RemoveAttrDefault(Oid relid, AttrNumber attnum,
  				  DropBehavior behavior, bool complain);
  extern void RemoveAttrDefaultById(Oid attrdefId);
! extern void RemoveStatistics(Oid relid, AttrNumber attnum);
  
  extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
  						  bool relhasoids);
--- 107,118 ----
  extern void RemoveAttrDefault(Oid relid, AttrNumber attnum,
  				  DropBehavior behavior, bool complain);
  extern void RemoveAttrDefaultById(Oid attrdefId);
! extern void AddStatistics(Oid relid, AttrNumber *attnums,
! 						  int n_attnums,
! 						  bool inherited,
! 						  int statistics_target);
! extern void InvalidateStatistics(Oid relid, AttrNumber attnum);
! extern void RemoveStatistics(Oid relid, AttrNumber *attnums, int n_attnums);
  
  extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
  						  bool relhasoids);
diff -dcrpN postgresql.orig/src/include/catalog/indexing.h postgresql.4/src/include/catalog/indexing.h
*** postgresql.orig/src/include/catalog/indexing.h	2011-07-24 18:16:45.286677978 +0200
--- postgresql.4/src/include/catalog/indexing.h	2011-07-27 15:33:44.519543035 +0200
*************** DECLARE_INDEX(pg_shdepend_depender_index
*** 218,225 ****
  DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
  #define SharedDependReferenceIndexId	1233
  
! DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_inh_index, 2696, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops, stainherit bool_ops));
! #define StatisticRelidAttnumInhIndexId	2696
  
  DECLARE_UNIQUE_INDEX(pg_tablespace_oid_index, 2697, on pg_tablespace using btree(oid oid_ops));
  #define TablespaceOidIndexId  2697
--- 218,225 ----
  DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
  #define SharedDependReferenceIndexId	1233
  
! DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_inh_index, 2696, on pg_statistic using btree(starelid oid_ops, staattnums int2vector_ops, stainherit bool_ops));
! #define StatisticRelidAttnumsInhIndexId	2696
  
  DECLARE_UNIQUE_INDEX(pg_tablespace_oid_index, 2697, on pg_tablespace using btree(oid oid_ops));
  #define TablespaceOidIndexId  2697
diff -dcrpN postgresql.orig/src/include/catalog/pg_amop.h postgresql.4/src/include/catalog/pg_amop.h
*** postgresql.orig/src/include/catalog/pg_amop.h	2011-04-11 15:36:27.235807013 +0200
--- postgresql.4/src/include/catalog/pg_amop.h	2011-07-27 15:13:09.785532127 +0200
*************** DATA(insert (	1991   30 30 4 s	648 403 0
*** 185,190 ****
--- 185,200 ----
  DATA(insert (	1991   30 30 5 s	646 403 0 ));
  
  /*
+  *	btree int2vector_ops
+  */
+ 
+ DATA(insert (	3097   22 22 1 s	199 403 0 ));
+ DATA(insert (	3097   22 22 2 s	322 403 0 ));
+ DATA(insert (	3097   22 22 3 s	386 403 0 ));
+ DATA(insert (	3097   22 22 4 s	323 403 0 ));
+ DATA(insert (	3097   22 22 5 s	276 403 0 ));
+ 
+ /*
   *	btree float_ops
   */
  
diff -dcrpN postgresql.orig/src/include/catalog/pg_amproc.h postgresql.4/src/include/catalog/pg_amproc.h
*** postgresql.orig/src/include/catalog/pg_amproc.h	2011-01-04 15:13:16.120551585 +0100
--- postgresql.4/src/include/catalog/pg_amproc.h	2011-07-27 15:13:09.793531431 +0200
*************** DATA(insert (	2233   703 703 1  380 ));
*** 123,128 ****
--- 123,129 ----
  DATA(insert (	2234   704 704 1  381 ));
  DATA(insert (	2789   27 27 1 2794 ));
  DATA(insert (	2968   2950 2950 1 2960 ));
+ DATA(insert (	3097   22 22 1 321 ));
  DATA(insert (	3522   3500 3500 1 3514 ));
  
  
diff -dcrpN postgresql.orig/src/include/catalog/pg_attribute.h postgresql.4/src/include/catalog/pg_attribute.h
*** postgresql.orig/src/include/catalog/pg_attribute.h	2011-02-10 10:36:32.321680466 +0100
--- postgresql.4/src/include/catalog/pg_attribute.h	2011-08-02 11:59:09.569791405 +0200
*************** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
*** 48,62 ****
  	Oid			atttypid;
  
  	/*
- 	 * attstattarget is the target number of statistics datapoints to collect
- 	 * during VACUUM ANALYZE of this column.  A zero here indicates that we do
- 	 * not wish to collect any stats about this column. A "-1" here indicates
- 	 * that no value has been explicitly set for this column, so ANALYZE
- 	 * should use the default setting.
- 	 */
- 	int4		attstattarget;
- 
- 	/*
  	 * attlen is a copy of the typlen field from pg_type for this attribute.
  	 * See atttypid comments above.
  	 */
--- 48,53 ----
*************** typedef FormData_pg_attribute *Form_pg_a
*** 179,205 ****
   * ----------------
   */
  
! #define Natts_pg_attribute				20
  #define Anum_pg_attribute_attrelid		1
  #define Anum_pg_attribute_attname		2
  #define Anum_pg_attribute_atttypid		3
! #define Anum_pg_attribute_attstattarget 4
! #define Anum_pg_attribute_attlen		5
! #define Anum_pg_attribute_attnum		6
! #define Anum_pg_attribute_attndims		7
! #define Anum_pg_attribute_attcacheoff	8
! #define Anum_pg_attribute_atttypmod		9
! #define Anum_pg_attribute_attbyval		10
! #define Anum_pg_attribute_attstorage	11
! #define Anum_pg_attribute_attalign		12
! #define Anum_pg_attribute_attnotnull	13
! #define Anum_pg_attribute_atthasdef		14
! #define Anum_pg_attribute_attisdropped	15
! #define Anum_pg_attribute_attislocal	16
! #define Anum_pg_attribute_attinhcount	17
! #define Anum_pg_attribute_attcollation	18
! #define Anum_pg_attribute_attacl		19
! #define Anum_pg_attribute_attoptions	20
  
  
  /* ----------------
--- 170,195 ----
   * ----------------
   */
  
! #define Natts_pg_attribute				19
  #define Anum_pg_attribute_attrelid		1
  #define Anum_pg_attribute_attname		2
  #define Anum_pg_attribute_atttypid		3
! #define Anum_pg_attribute_attlen		4
! #define Anum_pg_attribute_attnum		5
! #define Anum_pg_attribute_attndims		6
! #define Anum_pg_attribute_attcacheoff	7
! #define Anum_pg_attribute_atttypmod		8
! #define Anum_pg_attribute_attbyval		9
! #define Anum_pg_attribute_attstorage	10
! #define Anum_pg_attribute_attalign		11
! #define Anum_pg_attribute_attnotnull	12
! #define Anum_pg_attribute_atthasdef		13
! #define Anum_pg_attribute_attisdropped	14
! #define Anum_pg_attribute_attislocal	15
! #define Anum_pg_attribute_attinhcount	16
! #define Anum_pg_attribute_attcollation	17
! #define Anum_pg_attribute_attacl		18
! #define Anum_pg_attribute_attoptions	19
  
  
  /* ----------------
diff -dcrpN postgresql.orig/src/include/catalog/pg_class.h postgresql.4/src/include/catalog/pg_class.h
*** postgresql.orig/src/include/catalog/pg_class.h	2011-06-24 11:38:23.338906107 +0200
--- postgresql.4/src/include/catalog/pg_class.h	2011-08-02 14:03:43.862124993 +0200
*************** typedef FormData_pg_class *Form_pg_class
*** 132,138 ****
  /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
  DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
  DESCR("");
  DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
--- 132,138 ----
  /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
  DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 19 0 f f f f f 3 _null_ _null_ ));
  DESCR("");
  DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
diff -dcrpN postgresql.orig/src/include/catalog/pg_opclass.h postgresql.4/src/include/catalog/pg_opclass.h
*** postgresql.orig/src/include/catalog/pg_opclass.h	2011-01-04 15:13:16.125551330 +0100
--- postgresql.4/src/include/catalog/pg_opclass.h	2011-07-27 15:13:09.811529864 +0200
*************** DATA(insert (	403		bpchar_pattern_ops	PG
*** 158,163 ****
--- 158,164 ----
  DATA(insert (	403		money_ops			PGNSP PGUID 2099  790 t 0 ));
  DATA(insert (	405		bool_ops			PGNSP PGUID 2222   16 t 0 ));
  DATA(insert (	405		bytea_ops			PGNSP PGUID 2223   17 t 0 ));
+ DATA(insert (	403		int2vector_ops		PGNSP PGUID 3097   22 t 0 ));
  DATA(insert (	405		int2vector_ops		PGNSP PGUID 2224   22 t 0 ));
  DATA(insert (	403		tid_ops				PGNSP PGUID 2789   27 t 0 ));
  DATA(insert (	405		xid_ops				PGNSP PGUID 2225   28 t 0 ));
diff -dcrpN postgresql.orig/src/include/catalog/pg_operator.h postgresql.4/src/include/catalog/pg_operator.h
*** postgresql.orig/src/include/catalog/pg_operator.h	2011-06-06 09:12:54.421675553 +0200
--- postgresql.4/src/include/catalog/pg_operator.h	2011-07-27 15:13:09.822528906 +0200
*************** DATA(insert OID =  98 ( "="		   PGNSP PG
*** 134,139 ****
--- 134,150 ----
  DESCR("equal");
  #define TextEqualOperator	98
  
+ DATA(insert OID = 114 (  "<>"	   PGNSP PGUID b f f	22	22	16 114 386 int2vectorne neqsel neqjoinsel ));
+ DESCR("not equal");
+ DATA(insert OID = 199 (  "<"	   PGNSP PGUID b f f	22	22	16 276 323 int2vectorlt scalarltsel scalarltjoinsel ));
+ DESCR("less than");
+ DATA(insert OID = 276 (  ">"	   PGNSP PGUID b f f	22	22	16 199 322 int2vectorgt scalargtsel scalargtjoinsel ));
+ DESCR("greater than");
+ DATA(insert OID = 322 (  "<="	   PGNSP PGUID b f f	22	22	16 323 276 int2vectorle scalarltsel scalarltjoinsel ));
+ DESCR("less than or equal");
+ DATA(insert OID = 323 (  ">="	   PGNSP PGUID b f f	22	22	16 322 199 int2vectorge scalargtsel scalargtjoinsel ));
+ DESCR("greater than or equal");
+ 
  DATA(insert OID = 349 (  "||"	   PGNSP PGUID b f f 2277 2283 2277 0 0 array_append   -	   -	 ));
  DESCR("append element onto end of array");
  DATA(insert OID = 374 (  "||"	   PGNSP PGUID b f f 2283 2277 2277 0 0 array_prepend  -	   -	 ));
*************** DATA(insert OID = 389 (  "!!"	   PGNSP P
*** 151,157 ****
  DESCR("deprecated, use ! instead");
  DATA(insert OID = 385 (  "="	   PGNSP PGUID b f t	29	29	16 385	 0 cideq eqsel eqjoinsel ));
  DESCR("equal");
! DATA(insert OID = 386 (  "="	   PGNSP PGUID b f t	22	22	16 386	 0 int2vectoreq eqsel eqjoinsel ));
  DESCR("equal");
  
  DATA(insert OID = 387 (  "="	   PGNSP PGUID b t f	27	27	16 387 402 tideq eqsel eqjoinsel ));
--- 162,168 ----
  DESCR("deprecated, use ! instead");
  DATA(insert OID = 385 (  "="	   PGNSP PGUID b f t	29	29	16 385	 0 cideq eqsel eqjoinsel ));
  DESCR("equal");
! DATA(insert OID = 386 (  "="	   PGNSP PGUID b t t	22	22	16 386 114 int2vectoreq eqsel eqjoinsel ));
  DESCR("equal");
  
  DATA(insert OID = 387 (  "="	   PGNSP PGUID b t f	27	27	16 387 402 tideq eqsel eqjoinsel ));
diff -dcrpN postgresql.orig/src/include/catalog/pg_opfamily.h postgresql.4/src/include/catalog/pg_opfamily.h
*** postgresql.orig/src/include/catalog/pg_opfamily.h	2011-01-04 15:13:16.126551278 +0100
--- postgresql.4/src/include/catalog/pg_opfamily.h	2011-07-27 15:13:09.836527688 +0200
*************** DATA(insert OID = 2099 (	403		money_ops	
*** 114,119 ****
--- 114,120 ----
  DATA(insert OID = 2222 (	405		bool_ops		PGNSP PGUID ));
  #define BOOL_HASH_FAM_OID 2222
  DATA(insert OID = 2223 (	405		bytea_ops		PGNSP PGUID ));
+ DATA(insert OID = 3097 (	403		int2vector_ops	PGNSP PGUID ));
  DATA(insert OID = 2224 (	405		int2vector_ops	PGNSP PGUID ));
  DATA(insert OID = 2789 (	403		tid_ops			PGNSP PGUID ));
  DATA(insert OID = 2225 (	405		xid_ops			PGNSP PGUID ));
diff -dcrpN postgresql.orig/src/include/catalog/pg_proc.h postgresql.4/src/include/catalog/pg_proc.h
*** postgresql.orig/src/include/catalog/pg_proc.h	2011-07-18 15:42:00.078374691 +0200
--- postgresql.4/src/include/catalog/pg_proc.h	2011-07-27 15:27:18.053344124 +0200
*************** DESCR("length");
*** 216,221 ****
--- 216,228 ----
  DATA(insert OID = 1258 (  textcat		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 25 "25 25" _null_ _null_ _null_ _null_ textcat _null_ _null_ _null_ ));
  
  DATA(insert OID =  84 (  boolne			   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "16 16" _null_ _null_ _null_ _null_ boolne _null_ _null_ _null_ ));
+ 
+ DATA(insert OID =  86 (  int2vectorne		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "22 22" _null_ _null_ _null_ _null_ int2vectorne _null_ _null_ _null_ ));
+ DATA(insert OID =  87 (  int2vectorlt		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "22 22" _null_ _null_ _null_ _null_ int2vectorlt _null_ _null_ _null_ ));
+ DATA(insert OID =  88 (  int2vectorle		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "22 22" _null_ _null_ _null_ _null_ int2vectorle _null_ _null_ _null_ ));
+ DATA(insert OID =  90 (  int2vectorge		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "22 22" _null_ _null_ _null_ _null_ int2vectorge _null_ _null_ _null_ ));
+ DATA(insert OID = 3122 (  int2vectorgt		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "22 22" _null_ _null_ _null_ _null_ int2vectorgt _null_ _null_ _null_ ));
+ 
  DATA(insert OID =  89 (  version		   PGNSP PGUID 12 1 0 0 0 f f f t f s 0 0 25 "" _null_ _null_ _null_ _null_ pgsql_version _null_ _null_ _null_ ));
  DESCR("PostgreSQL version string");
  
*************** DESCR("I/O");
*** 566,571 ****
--- 573,580 ----
  
  DATA(insert OID = 350 (  btint2cmp		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "21 21" _null_ _null_ _null_ _null_ btint2cmp _null_ _null_ _null_ ));
  DESCR("less-equal-greater");
+ DATA(insert OID = 321 (  btint2vectorcmp	   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "22 22" _null_ _null_ _null_ _null_ btint2vectorcmp _null_ _null_ _null_ ));
+ DESCR("less-equal-greater");
  DATA(insert OID = 351 (  btint4cmp		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "23 23" _null_ _null_ _null_ _null_ btint4cmp _null_ _null_ _null_ ));
  DESCR("less-equal-greater");
  DATA(insert OID = 842 (  btint8cmp		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "20 20" _null_ _null_ _null_ _null_ btint8cmp _null_ _null_ _null_ ));
diff -dcrpN postgresql.orig/src/include/catalog/pg_statistic.h postgresql.4/src/include/catalog/pg_statistic.h
*** postgresql.orig/src/include/catalog/pg_statistic.h	2011-02-22 18:51:42.762512469 +0100
--- postgresql.4/src/include/catalog/pg_statistic.h	2011-08-02 11:49:16.758358984 +0200
***************
*** 40,50 ****
  
  CATALOG(pg_statistic,2619) BKI_WITHOUT_OIDS
  {
! 	/* These fields form the unique key for the entry: */
  	Oid			starelid;		/* relation containing attribute */
- 	int2		staattnum;		/* attribute (column) stats are for */
  	bool		stainherit;		/* true if inheritance children are included */
  
  	/* the fraction of the column's entries that are NULL: */
  	float4		stanullfrac;
  
--- 40,63 ----
  
  CATALOG(pg_statistic,2619) BKI_WITHOUT_OIDS
  {
! 	/*
! 	 * These fields (together with the staattnums field below in the variable fields)
! 	 * form the unique key for the entry:
! 	 */
  	Oid			starelid;		/* relation containing attribute */
  	bool		stainherit;		/* true if inheritance children are included */
  
+ 	/* this entry is valid */
+ 	bool		stavalid;
+ 
+ 	/* statarget is the target number of statistics datapoints to collect
+ 	 * during VACUUM ANALYZE of this column.  A zero here indicates that we do
+ 	 * not wish to collect any stats about this column. A "-1" here indicates
+ 	 * that no value has been explicitly set for this column, so ANALYZE
+ 	 * should use the default setting.
+ 	 */
+ 	int4		statarget;
+ 
  	/* the fraction of the column's entries that are NULL: */
  	float4		stanullfrac;
  
*************** CATALOG(pg_statistic,2619) BKI_WITHOUT_O
*** 110,115 ****
--- 123,129 ----
  	 * the full field access machinery (heap_getattr) for them.  We declare
  	 * them here for the catalog machinery.
  	 */
+ 	int2vector		staattnums;		/* attributes (columns) stats are for */
  
  	float4		stanumbers1[1];
  	float4		stanumbers2[1];
*************** typedef FormData_pg_statistic *Form_pg_s
*** 143,171 ****
   *		compiler constants for pg_statistic
   * ----------------
   */
! #define Natts_pg_statistic				22
  #define Anum_pg_statistic_starelid		1
! #define Anum_pg_statistic_staattnum		2
! #define Anum_pg_statistic_stainherit	3
! #define Anum_pg_statistic_stanullfrac	4
! #define Anum_pg_statistic_stawidth		5
! #define Anum_pg_statistic_stadistinct	6
! #define Anum_pg_statistic_stakind1		7
! #define Anum_pg_statistic_stakind2		8
! #define Anum_pg_statistic_stakind3		9
! #define Anum_pg_statistic_stakind4		10
! #define Anum_pg_statistic_staop1		11
! #define Anum_pg_statistic_staop2		12
! #define Anum_pg_statistic_staop3		13
! #define Anum_pg_statistic_staop4		14
! #define Anum_pg_statistic_stanumbers1	15
! #define Anum_pg_statistic_stanumbers2	16
! #define Anum_pg_statistic_stanumbers3	17
! #define Anum_pg_statistic_stanumbers4	18
! #define Anum_pg_statistic_stavalues1	19
! #define Anum_pg_statistic_stavalues2	20
! #define Anum_pg_statistic_stavalues3	21
! #define Anum_pg_statistic_stavalues4	22
  
  /*
   * Currently, three statistical slot "kinds" are defined: most common values,
--- 157,187 ----
   *		compiler constants for pg_statistic
   * ----------------
   */
! #define Natts_pg_statistic				24
  #define Anum_pg_statistic_starelid		1
! #define Anum_pg_statistic_stainherit		2
! #define Anum_pg_statistic_stavalid		3
! #define Anum_pg_statistic_statarget		4
! #define Anum_pg_statistic_stanullfrac		5
! #define Anum_pg_statistic_stawidth		6
! #define Anum_pg_statistic_stadistinct		7
! #define Anum_pg_statistic_stakind1		8
! #define Anum_pg_statistic_stakind2		9
! #define Anum_pg_statistic_stakind3		10
! #define Anum_pg_statistic_stakind4		11
! #define Anum_pg_statistic_staop1		12
! #define Anum_pg_statistic_staop2		13
! #define Anum_pg_statistic_staop3		14
! #define Anum_pg_statistic_staop4		15
! #define Anum_pg_statistic_staattnums		16
! #define Anum_pg_statistic_stanumbers1	17
! #define Anum_pg_statistic_stanumbers2	18
! #define Anum_pg_statistic_stanumbers3	19
! #define Anum_pg_statistic_stanumbers4	20
! #define Anum_pg_statistic_stavalues1	21
! #define Anum_pg_statistic_stavalues2	22
! #define Anum_pg_statistic_stavalues3	23
! #define Anum_pg_statistic_stavalues4	24
  
  /*
   * Currently, three statistical slot "kinds" are defined: most common values,
diff -dcrpN postgresql.orig/src/include/commands/vacuum.h postgresql.4/src/include/commands/vacuum.h
*** postgresql.orig/src/include/commands/vacuum.h	2011-06-02 10:21:24.006634564 +0200
--- postgresql.4/src/include/commands/vacuum.h	2011-08-02 11:49:16.768358249 +0200
*************** typedef struct VacAttrStats
*** 74,79 ****
--- 74,83 ----
  	 * information about the datatype being fed to the typanalyze function.
  	 */
  	Form_pg_attribute attr;		/* copy of pg_attribute row for column */
+ 	int4		statarget;	/* effective statistics target */
+ 	int4		oldtarget;	/* statistics target in pg_statistic
+ 					   this must be kept so the -1 to denote
+ 					   default_statistics_target is not overwritten */
  	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 */
diff -dcrpN postgresql.orig/src/include/utils/builtins.h postgresql.4/src/include/utils/builtins.h
*** postgresql.orig/src/include/utils/builtins.h	2011-06-24 11:38:23.342905815 +0200
--- postgresql.4/src/include/utils/builtins.h	2011-07-27 15:13:09.893522727 +0200
*************** extern Datum int2vectorout(PG_FUNCTION_A
*** 174,179 ****
--- 174,184 ----
  extern Datum int2vectorrecv(PG_FUNCTION_ARGS);
  extern Datum int2vectorsend(PG_FUNCTION_ARGS);
  extern Datum int2vectoreq(PG_FUNCTION_ARGS);
+ extern Datum int2vectorne(PG_FUNCTION_ARGS);
+ extern Datum int2vectorlt(PG_FUNCTION_ARGS);
+ extern Datum int2vectorle(PG_FUNCTION_ARGS);
+ extern Datum int2vectorgt(PG_FUNCTION_ARGS);
+ extern Datum int2vectorge(PG_FUNCTION_ARGS);
  extern Datum int4in(PG_FUNCTION_ARGS);
  extern Datum int4out(PG_FUNCTION_ARGS);
  extern Datum int4recv(PG_FUNCTION_ARGS);
*************** extern void pg_lltoa(int64 ll, char *a);
*** 283,288 ****
--- 288,294 ----
   */
  extern Datum btboolcmp(PG_FUNCTION_ARGS);
  extern Datum btint2cmp(PG_FUNCTION_ARGS);
+ extern Datum btint2vectorcmp(PG_FUNCTION_ARGS);
  extern Datum btint4cmp(PG_FUNCTION_ARGS);
  extern Datum btint8cmp(PG_FUNCTION_ARGS);
  extern Datum btfloat4cmp(PG_FUNCTION_ARGS);
diff -dcrpN postgresql.orig/src/include/utils/selfuncs.h postgresql.4/src/include/utils/selfuncs.h
*** postgresql.orig/src/include/utils/selfuncs.h	2011-06-10 11:06:01.495860021 +0200
--- postgresql.4/src/include/utils/selfuncs.h	2011-08-02 11:49:16.788356778 +0200
*************** typedef bool (*get_index_stats_hook_type
*** 110,115 ****
--- 110,119 ----
  												  VariableStatData *vardata);
  extern PGDLLIMPORT get_index_stats_hook_type get_index_stats_hook;
  
+ extern void validate_statistics(VariableStatData *vardata,
+ 						 Oid relid,
+ 						 AttrNumber *attnums, int n_attnums,
+ 						 bool inherited);
  extern void examine_variable(PlannerInfo *root, Node *node, int varRelid,
  				 VariableStatData *vardata);
  extern bool get_restriction_variable(PlannerInfo *root, List *args,
diff -dcrpN postgresql.orig/src/test/regress/expected/rules.out postgresql.4/src/test/regress/expected/rules.out
*** postgresql.orig/src/test/regress/expected/rules.out	2011-07-24 18:16:45.313676620 +0200
--- postgresql.4/src/test/regress/expected/rules.out	2011-07-27 15:33:44.546540690 +0200
*************** SELECT viewname, definition FROM pg_view
*** 1317,1323 ****
   pg_statio_user_indexes          | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text));
   pg_statio_user_sequences        | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text));
   pg_statio_user_tables           | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text));
!  pg_stats                        | SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, s.stainherit AS inherited, s.stanullfrac AS null_frac, s.stawidth AS avg_width, s.stadistinct AS n_distinct, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stavalues1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stavalues2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stavalues3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stavalues4 ELSE NULL::anyarray END AS most_common_vals, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stanumbers1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stanumbers2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stanumbers3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (s.stakind1 = 2) THEN s.stavalues1 WHEN (s.stakind2 = 2) THEN s.stavalues2 WHEN (s.stakind3 = 2) THEN s.stavalues3 WHEN (s.stakind4 = 2) THEN s.stavalues4 ELSE NULL::anyarray END AS histogram_bounds, CASE WHEN (s.stakind1 = 3) THEN s.stanumbers1[1] WHEN (s.stakind2 = 3) THEN s.stanumbers2[1] WHEN (s.stakind3 = 3) THEN s.stanumbers3[1] WHEN (s.stakind4 = 3) THEN s.stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text));
   pg_tables                       | SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, t.spcname AS tablespace, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, c.relhastriggers AS hastriggers FROM ((pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = 'r'::"char");
   pg_timezone_abbrevs             | SELECT pg_timezone_abbrevs.abbrev, pg_timezone_abbrevs.utc_offset, pg_timezone_abbrevs.is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst);
   pg_timezone_names               | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst);
--- 1317,1323 ----
   pg_statio_user_indexes          | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text));
   pg_statio_user_sequences        | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text));
   pg_statio_user_tables           | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text));
!  pg_stats                        | SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, s.stainherit AS inherited, s.stanullfrac AS null_frac, s.stawidth AS avg_width, s.stadistinct AS n_distinct, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stavalues1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stavalues2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stavalues3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stavalues4 ELSE NULL::anyarray END AS most_common_vals, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stanumbers1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stanumbers2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stanumbers3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (s.stakind1 = 2) THEN s.stavalues1 WHEN (s.stakind2 = 2) THEN s.stavalues2 WHEN (s.stakind3 = 2) THEN s.stavalues3 WHEN (s.stakind4 = 2) THEN s.stavalues4 ELSE NULL::anyarray END AS histogram_bounds, CASE WHEN (s.stakind1 = 3) THEN s.stanumbers1[1] WHEN (s.stakind2 = 3) THEN s.stanumbers2[1] WHEN (s.stakind3 = 3) THEN s.stanumbers3[1] WHEN (s.stakind4 = 3) THEN s.stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON ((((c.oid = a.attrelid) AND (array_length(s.staattnums, 1) = 1)) AND (a.attnum = s.staattnums[0])))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text));
   pg_tables                       | SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, t.spcname AS tablespace, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, c.relhastriggers AS hastriggers FROM ((pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = 'r'::"char");
   pg_timezone_abbrevs             | SELECT pg_timezone_abbrevs.abbrev, pg_timezone_abbrevs.utc_offset, pg_timezone_abbrevs.is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst);
   pg_timezone_names               | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst);