diff -dcrpN postgresql.orig/src/backend/access/common/tupdesc.c postgresql/src/backend/access/common/tupdesc.c *** postgresql.orig/src/backend/access/common/tupdesc.c 2011-08-07 11:29:16.011256533 +0200 --- postgresql/src/backend/access/common/tupdesc.c 2011-09-12 10:01:16.383515838 +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/src/backend/access/nbtree/nbtcompare.c *** postgresql.orig/src/backend/access/nbtree/nbtcompare.c 2011-01-04 15:13:15.816567224 +0100 --- postgresql/src/backend/access/nbtree/nbtcompare.c 2011-09-12 10:01:16.384515791 +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/src/backend/bootstrap/bootstrap.c *** postgresql.orig/src/backend/bootstrap/bootstrap.c 2011-09-12 09:54:31.169314275 +0200 --- postgresql/src/backend/bootstrap/bootstrap.c 2011-09-12 10:01:16.408514672 +0200 *************** DefineAttr(char *name, char *type, int a *** 728,734 **** attrtypes[attnum]->attndims = 0; } - attrtypes[attnum]->attstattarget = -1; attrtypes[attnum]->attcacheoff = -1; attrtypes[attnum]->atttypmod = -1; attrtypes[attnum]->attislocal = true; --- 728,733 ---- diff -dcrpN postgresql.orig/src/backend/catalog/heap.c postgresql/src/backend/catalog/heap.c *** postgresql.orig/src/backend/catalog/heap.c 2011-09-02 12:52:41.334576388 +0200 --- postgresql/src/backend/catalog/heap.c 2011-09-12 10:01:16.429513691 +0200 *************** static List *insert_ordered_unique_oid(L *** 126,162 **** */ 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 }; --- 126,162 ---- */ 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 = { *** 168,174 **** * 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 }; --- 168,174 ---- * 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 *** 595,601 **** 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); --- 595,600 ---- *************** AddNewAttributeTuples(Oid new_rel_oid, *** 667,678 **** 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; --- 666,678 ---- 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 *** 1486,1494 **** /* 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 */ --- 1486,1491 ---- *************** RemoveAttributeById(Oid relid, AttrNumbe *** 1510,1517 **** heap_close(attr_rel, RowExclusiveLock); if (attnum > 0) ! RemoveStatistics(relid, attnum); relation_close(rel, NoLock); } --- 1507,1515 ---- 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) *** 1735,1741 **** /* * delete statistics */ ! RemoveStatistics(relid, 0); /* * delete attribute tuples --- 1733,1739 ---- /* * delete statistics */ ! RemoveStatistics(relid, NULL, 0); /* * delete attribute tuples *************** cookConstraint(ParseState *pstate, *** 2516,2534 **** /* ! * 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); --- 2514,2727 ---- /* ! * 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; ! ! 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 *** 2537,2554 **** 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 */ --- 2730,2748 ---- 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 *** 2557,2562 **** --- 2751,2759 ---- systable_endscan(scan); + if (attnumvector) + pfree(attnumvector); + heap_close(pgstatistic, RowExclusiveLock); } diff -dcrpN postgresql.orig/src/backend/catalog/index.c postgresql/src/backend/catalog/index.c *** postgresql.orig/src/backend/catalog/index.c 2011-09-05 15:08:06.485569429 +0200 --- postgresql/src/backend/catalog/index.c 2011-09-12 10:01:16.461512196 +0200 *************** ConstructTupleDescriptor(Relation heapRe *** 342,348 **** */ to->attnum = i + 1; - to->attstattarget = -1; to->attcacheoff = -1; to->attnotnull = false; to->atthasdef = false; --- 342,347 ---- *************** ConstructTupleDescriptor(Relation heapRe *** 380,386 **** to->attbyval = typeTup->typbyval; to->attstorage = typeTup->typstorage; to->attalign = typeTup->typalign; - to->attstattarget = -1; to->attcacheoff = -1; to->atttypmod = -1; to->attislocal = true; --- 379,384 ---- *************** index_drop(Oid indexId) *** 1352,1358 **** * them. */ if (hasexprs) ! RemoveStatistics(indexId, 0); /* * fix ATTRIBUTE relation --- 1350,1356 ---- * them. */ if (hasexprs) ! RemoveStatistics(indexId, NULL, 0); /* * fix ATTRIBUTE relation diff -dcrpN postgresql.orig/src/backend/catalog/system_views.sql postgresql/src/backend/catalog/system_views.sql *** postgresql.orig/src/backend/catalog/system_views.sql 2011-07-24 18:16:45.262679185 +0200 --- postgresql/src/backend/catalog/system_views.sql 2011-09-12 10:01:16.495510607 +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/src/backend/commands/analyze.c *** postgresql.orig/src/backend/commands/analyze.c 2011-09-12 09:54:31.169314275 +0200 --- postgresql/src/backend/commands/analyze.c 2011-09-13 11:15:14.977046160 +0200 *************** *** 20,31 **** --- 20,34 ---- #include "access/tupconvert.h" #include "access/tuptoaster.h" #include "access/xact.h" + #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" + #include "catalog/namespace.h" #include "catalog/pg_collation.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" + #include "commands/defrem.h" #include "commands/tablecmds.h" #include "commands/vacuum.h" #include "executor/executor.h" *************** *** 41,47 **** --- 44,52 ---- #include "storage/procarray.h" #include "utils/acl.h" #include "utils/attoptcache.h" + #include "utils/builtins.h" #include "utils/datum.h" + #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" *************** typedef struct AnlIndexData *** 68,75 **** { IndexInfo *indexInfo; /* BuildIndexInfo result */ double tupleFract; /* fraction of rows for partial index */ ! VacAttrStats **vacattrstats; /* index attrs to analyze */ ! int attr_cnt; } AnlIndexData; --- 73,82 ---- { IndexInfo *indexInfo; /* BuildIndexInfo result */ double tupleFract; /* fraction of rows for partial index */ ! VacStats **vacstats; /* index attrs to analyze */ ! int stats_cnt; /* this many stats we need to compute - single and cross-column stats included */ ! int attr_cnt; /* the relation has attr_cnt number of columns */ ! bool has_xcol_stats; } AnlIndexData; *************** static void compute_index_stats(Relation *** 93,100 **** 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); static double random_fract(void); --- 100,107 ---- AnlIndexData *indexdata, int nindexes, HeapTuple *rows, int numrows, MemoryContext col_context); ! static VacStats *examine_attribute(Relation onerel, int2vector *attnums, bool inh, ! Node **index_expr); static int acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows); static double random_fract(void); *************** static int acquire_inherited_sample_rows *** 105,116 **** HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows); static void update_attstats(Oid relid, bool inh, ! int natts, VacAttrStats **vacattrstats); ! static Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); ! static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); ! static bool std_typanalyze(VacAttrStats *stats); /* * analyze_rel() -- analyze one relation --- 112,129 ---- HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows); static void update_attstats(Oid relid, bool inh, ! int natts, VacStats **vacstats); ! static Datum std_fetch_func(VacStatsP stats, int rownum, AttrNumber tupattnum, ! bool *isNull); ! static Datum ind_fetch_func(VacStatsP stats, int rownum, AttrNumber tupattnum, ! bool *isNull); ! static bool std_typanalyze(VacStats *stats, int index); + static int ordered_findval(VacStatsP stats, int rownum, int index, AnalyzeAttrFetchFunc fetchfunc, void *arg); + static int unordered_findval(VacStatsP stats, int rownum, int index, AnalyzeAttrFetchFunc fetchfunc, void *arg); + static void compute_cross_column_stats(VacStatsP stats, AnalyzeAttrFetchFunc fetchfunc, + int numrows); /* * analyze_rel() -- analyze one relation *************** analyze_rel(Oid relid, VacuumStmt *vacst *** 253,272 **** LWLockRelease(ProcArrayLock); } /* * do_analyze_rel() -- analyze one relation, recursively or not */ static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, bool inh) { ! int attr_cnt, tcnt, i, ind; Relation *Irel; int nindexes; bool hasindex; ! VacAttrStats **vacattrstats; AnlIndexData *indexdata; int targrows, numrows; --- 266,343 ---- LWLockRelease(ProcArrayLock); } + static int + get_extrastats_count(Oid relid, int2vector ***out_attnumvectors) + { + Relation pgstatistic; + TupleDesc tupDesc; + SysScanDesc scan; + ScanKeyData key; + HeapTuple tuple; + int count = 0; + bool isnull; + int2vector *attnumvector = NULL; + int2vector **attnumvectors = NULL; + + pgstatistic = heap_open(StatisticRelationId, RowExclusiveLock); + tupDesc = RelationGetDescr(pgstatistic); + + ScanKeyInit(&key, + Anum_pg_statistic_starelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + scan = systable_beginscan(pgstatistic, StatisticRelidAttnumsInhIndexId, true, + SnapshotNow, 1, &key); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + Datum attnvec = heap_getattr(tuple, Anum_pg_statistic_staattnums, tupDesc, &isnull); + + Assert(!isnull); + + attnumvector = (int2vector *) DatumGetPointer(attnvec); + + if (attnumvector->dim1 > 1) + { + count++; + if (out_attnumvectors) + { + if (attnumvectors == NULL) + attnumvectors = palloc(count * sizeof(int2vector *)); + else + attnumvectors = repalloc(attnumvectors, count * sizeof(int2vector *)); + attnumvectors[count - 1] = (int2vector *)datumCopy(attnvec, false, -1); + } + } + } + + systable_endscan(scan); + + heap_close(pgstatistic, RowExclusiveLock); + + if (out_attnumvectors) + *out_attnumvectors = attnumvectors; + + return count; + } + /* * do_analyze_rel() -- analyze one relation, recursively or not */ static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, bool inh) { ! int stats_cnt, ! extrastats_cnt, tcnt, i, ind; Relation *Irel; int nindexes; bool hasindex; ! VacStats **vacstats; ! int2vector **extrastats_attnums; AnlIndexData *indexdata; int targrows, numrows; *************** do_analyze_rel(Relation onerel, VacuumSt *** 329,366 **** { ListCell *le; ! vacattrstats = (VacAttrStats **) palloc(list_length(vacstmt->va_cols) * ! sizeof(VacAttrStats *)); tcnt = 0; foreach(le, vacstmt->va_cols) { char *col = strVal(lfirst(le)); ! i = attnameAttNum(onerel, col, false); ! if (i == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", col, RelationGetRelationName(onerel)))); ! vacattrstats[tcnt] = examine_attribute(onerel, i, NULL); ! if (vacattrstats[tcnt] != NULL) tcnt++; } ! attr_cnt = tcnt; } else { ! attr_cnt = onerel->rd_att->natts; ! vacattrstats = (VacAttrStats **) ! palloc(attr_cnt * sizeof(VacAttrStats *)); tcnt = 0; ! for (i = 1; i <= attr_cnt; i++) { ! vacattrstats[tcnt] = examine_attribute(onerel, i, NULL); ! if (vacattrstats[tcnt] != NULL) tcnt++; } ! attr_cnt = tcnt; } /* --- 400,474 ---- { ListCell *le; ! vacstats = (VacStats **) palloc(list_length(vacstmt->va_cols) * ! sizeof(VacStats *)); tcnt = 0; foreach(le, vacstmt->va_cols) { char *col = strVal(lfirst(le)); + AttrNumber attnum; + int2vector *attnums; ! attnum = attnameAttNum(onerel, col, false); ! if (attnum == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", col, RelationGetRelationName(onerel)))); ! attnums = buildint2vector(&attnum, 1); ! vacstats[tcnt] = examine_attribute(onerel, attnums, inh, NULL); ! if (vacstats[tcnt] != NULL) tcnt++; + else + pfree(attnums); } ! stats_cnt = tcnt; ! extrastats_cnt = 0; } else { ! /* ! * Process all attributes of the relation unconditionally ! * as system tables may not have pg_statistic entries at ! * this point, they will be created in update_attstats(). ! */ ! stats_cnt = onerel->rd_att->natts; ! ! extrastats_cnt = get_extrastats_count(onerel->rd_id, &extrastats_attnums); ! ! vacstats = (VacStats **) ! palloc((stats_cnt + extrastats_cnt) * sizeof(VacStats *)); tcnt = 0; ! for (i = 1; i <= stats_cnt; i++) { ! AttrNumber attnum = i; ! int2vector *attnums; ! ! attnums = buildint2vector(&attnum, 1); ! ! vacstats[tcnt] = examine_attribute(onerel, attnums, inh, NULL); ! if (vacstats[tcnt] != NULL) tcnt++; + else + pfree(attnums); } ! ! /* Now process the extra statistics */ ! if (extrastats_attnums) ! { ! for (i = 0; extrastats_attnums && i < extrastats_cnt; i++) ! { ! vacstats[tcnt] = examine_attribute(onerel, extrastats_attnums[i], inh, NULL); ! if (vacstats[tcnt] != NULL) ! tcnt++; ! else ! pfree(extrastats_attnums[i]); ! } ! pfree(extrastats_attnums); ! extrastats_attnums = NULL; ! } ! ! stats_cnt = tcnt; } /* *************** do_analyze_rel(Relation onerel, VacuumSt *** 389,401 **** thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind]); thisdata->tupleFract = 1.0; /* fix later if partial */ if (indexInfo->ii_Expressions != NIL && vacstmt->va_cols == NIL) { ListCell *indexpr_item = list_head(indexInfo->ii_Expressions); ! thisdata->vacattrstats = (VacAttrStats **) ! palloc(indexInfo->ii_NumIndexAttrs * sizeof(VacAttrStats *)); ! tcnt = 0; for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { int keycol = indexInfo->ii_KeyAttrNumbers[i]; --- 497,516 ---- thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind]); thisdata->tupleFract = 1.0; /* fix later if partial */ + + tcnt = 0; + extrastats_cnt = get_extrastats_count(Irel[ind]->rd_id, &extrastats_attnums); + + /* + * Process expression index single-attribute statistics. + */ if (indexInfo->ii_Expressions != NIL && vacstmt->va_cols == NIL) { ListCell *indexpr_item = list_head(indexInfo->ii_Expressions); ! thisdata->vacstats = (VacStats **) ! palloc((indexInfo->ii_NumIndexAttrs + extrastats_cnt) * sizeof(VacStats *)); ! for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { int keycol = indexInfo->ii_KeyAttrNumbers[i]; *************** do_analyze_rel(Relation onerel, VacuumSt *** 404,422 **** { /* Found an index expression */ Node *indexkey; if (indexpr_item == NULL) /* shouldn't happen */ elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); indexpr_item = lnext(indexpr_item); ! thisdata->vacattrstats[tcnt] = ! examine_attribute(Irel[ind], i + 1, indexkey); ! if (thisdata->vacattrstats[tcnt] != NULL) tcnt++; } } ! thisdata->attr_cnt = tcnt; } } } --- 519,592 ---- { /* Found an index expression */ Node *indexkey; + AttrNumber attnum; + int2vector *attnums; if (indexpr_item == NULL) /* shouldn't happen */ elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); indexpr_item = lnext(indexpr_item); ! ! attnum = i + 1; ! attnums = buildint2vector(&attnum, 1); ! ! thisdata->vacstats[tcnt] = ! examine_attribute(Irel[ind], attnums, inh, &indexkey); ! if (thisdata->vacstats[tcnt] != NULL) tcnt++; + else + pfree(attnums); } } ! } + + /* + * Process cross-column statistics. There can only be at most one + * multi-column statistics for an index that covers every attributes. + */ + if (extrastats_attnums && vacstmt->va_cols == NIL) + { + Node *indexkeys[STATISTIC_NUM_SLOTS]; + + if (thisdata->vacstats == NULL) + thisdata->vacstats = (VacStats **) palloc(sizeof(VacStats *)); + + /* + * Don't process indexes with too many attributes. + * CREATE EXTRA STATISTICS bails out for such a case + * but better safe than sorry. + */ + if (indexInfo->ii_NumIndexAttrs <= STATISTIC_NUM_SLOTS) + { + ListCell *indexpr_item = list_head(indexInfo->ii_Expressions); + + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + int keycol = indexInfo->ii_KeyAttrNumbers[i]; + if (keycol == 0) + { + indexkeys[i] = (Node *) lfirst(indexpr_item); + indexpr_item = lnext(indexpr_item); + } + else + indexkeys[i] = NULL; + } + + thisdata->vacstats[tcnt] = examine_attribute(onerel, extrastats_attnums[0], inh, indexkeys); + if (thisdata->vacstats[tcnt] != NULL) + tcnt++; + else + pfree(extrastats_attnums[i]); + } + pfree(extrastats_attnums); + extrastats_attnums = NULL; + + thisdata->has_xcol_stats = true; + } + + thisdata->stats_cnt = tcnt; + thisdata->attr_cnt = indexInfo->ii_NumIndexAttrs; } } *************** do_analyze_rel(Relation onerel, VacuumSt *** 427,445 **** * the target in the corner case where there are no analyzable columns.) */ targrows = 100; ! for (i = 0; i < attr_cnt; i++) { ! if (targrows < vacattrstats[i]->minrows) ! targrows = vacattrstats[i]->minrows; } for (ind = 0; ind < nindexes; ind++) { AnlIndexData *thisdata = &indexdata[ind]; ! for (i = 0; i < thisdata->attr_cnt; i++) { ! if (targrows < thisdata->vacattrstats[i]->minrows) ! targrows = thisdata->vacattrstats[i]->minrows; } } --- 597,615 ---- * the target in the corner case where there are no analyzable columns.) */ targrows = 100; ! for (i = 0; i < stats_cnt; i++) { ! if (targrows < vacstats[i]->minrows) ! targrows = vacstats[i]->minrows; } for (ind = 0; ind < nindexes; ind++) { AnlIndexData *thisdata = &indexdata[ind]; ! for (i = 0; i < thisdata->stats_cnt; i++) { ! if (targrows < thisdata->vacstats[i]->minrows) ! targrows = thisdata->vacstats[i]->minrows; } } *************** do_analyze_rel(Relation onerel, VacuumSt *** 457,463 **** /* * Compute the statistics. Temporary results during the calculations for * each column are stored in a child context. The calc routines are ! * responsible to make sure that whatever they store into the VacAttrStats * structure is allocated in anl_context. */ if (numrows > 0) --- 627,633 ---- /* * Compute the statistics. Temporary results during the calculations for * each column are stored in a child context. The calc routines are ! * responsible to make sure that whatever they store into the VacStats * structure is allocated in anl_context. */ if (numrows > 0) *************** do_analyze_rel(Relation onerel, VacuumSt *** 472,504 **** ALLOCSET_DEFAULT_MAXSIZE); old_context = MemoryContextSwitchTo(col_context); ! for (i = 0; i < attr_cnt; i++) { ! VacAttrStats *stats = vacattrstats[i]; ! AttributeOpts *aopt = ! get_attribute_options(onerel->rd_id, stats->attr->attnum); stats->rows = rows; stats->tupDesc = onerel->rd_att; ! (*stats->compute_stats) (stats, std_fetch_func, numrows, totalrows); ! /* ! * If the appropriate flavor of the n_distinct option is ! * specified, override with the corresponding value. ! */ ! if (aopt != NULL) ! { ! float8 n_distinct = ! inh ? aopt->n_distinct_inherited : aopt->n_distinct; ! if (n_distinct != 0.0) ! stats->stadistinct = n_distinct; } MemoryContextResetAndDeleteChildren(col_context); } if (hasindex) --- 642,683 ---- ALLOCSET_DEFAULT_MAXSIZE); old_context = MemoryContextSwitchTo(col_context); ! for (i = 0; i < stats_cnt; i++) { ! VacStats *stats = vacstats[i]; ! AttributeOpts *aopt; ! int j; stats->rows = rows; stats->tupDesc = onerel->rd_att; ! ! for (j = 0; j < stats->attnums->dim1; j++) ! { ! aopt = get_attribute_options(onerel->rd_id, stats->attnums->values[j]); ! ! (*stats->statfuncs[j].compute_func_ptr) (stats, ! j, std_fetch_func, numrows, totalrows); ! /* ! * If the appropriate flavor of the n_distinct option is ! * specified, override with the corresponding value. ! */ ! if (aopt != NULL) ! { ! float8 n_distinct = ! inh ? aopt->n_distinct_inherited : aopt->n_distinct; ! if (n_distinct != 0.0) ! stats->stadistinct[j] = n_distinct; ! } } MemoryContextResetAndDeleteChildren(col_context); + + compute_cross_column_stats(stats, std_fetch_func, numrows); } if (hasindex) *************** do_analyze_rel(Relation onerel, VacuumSt *** 516,529 **** * pg_statistic for columns we didn't process, we leave them alone.) */ update_attstats(RelationGetRelid(onerel), inh, ! attr_cnt, vacattrstats); for (ind = 0; ind < nindexes; ind++) { AnlIndexData *thisdata = &indexdata[ind]; update_attstats(RelationGetRelid(Irel[ind]), false, ! thisdata->attr_cnt, thisdata->vacattrstats); } } --- 695,711 ---- * pg_statistic for columns we didn't process, we leave them alone.) */ update_attstats(RelationGetRelid(onerel), inh, ! stats_cnt, vacstats); for (ind = 0; ind < nindexes; ind++) { AnlIndexData *thisdata = &indexdata[ind]; + for (i = 0; i < thisdata->stats_cnt; i++) + compute_cross_column_stats(thisdata->vacstats[i], ind_fetch_func, numrows); + update_attstats(RelationGetRelid(Irel[ind]), false, ! thisdata->stats_cnt, thisdata->vacstats); } } *************** compute_index_stats(Relation onerel, dou *** 627,632 **** --- 809,816 ---- old_context; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; + bool typbyval[INDEX_MAX_KEYS]; + int16 typlen[INDEX_MAX_KEYS]; int ind, i; *************** compute_index_stats(Relation onerel, dou *** 641,647 **** { AnlIndexData *thisdata = &indexdata[ind]; IndexInfo *indexInfo = thisdata->indexInfo; ! int attr_cnt = thisdata->attr_cnt; TupleTableSlot *slot; EState *estate; ExprContext *econtext; --- 825,831 ---- { AnlIndexData *thisdata = &indexdata[ind]; IndexInfo *indexInfo = thisdata->indexInfo; ! int stats_cnt = thisdata->stats_cnt; TupleTableSlot *slot; EState *estate; ExprContext *econtext; *************** compute_index_stats(Relation onerel, dou *** 652,662 **** tcnt, rowno; double totalindexrows; /* Ignore index if no columns to analyze and not partial */ ! if (attr_cnt == 0 && indexInfo->ii_Predicate == NIL) continue; /* * Need an EState for evaluation of index expressions and * partial-index predicates. Create it in the per-index context to be --- 836,865 ---- tcnt, rowno; double totalindexrows; + ListCell *indexpr_item = list_head(indexInfo->ii_Expressions); /* Ignore index if no columns to analyze and not partial */ ! if (stats_cnt == 0 && indexInfo->ii_Predicate == NIL) continue; + for (i = 0; i < thisdata->attr_cnt; i++) + { + int keycol = indexInfo->ii_KeyAttrNumbers[i]; + + if (keycol == 0) + { + Node *indexkey = (Node *) lfirst(indexpr_item); + + get_typlenbyval(exprType(indexkey), &typlen[i], &typbyval[i]); + indexpr_item = lnext(indexpr_item); + } + else + { + typbyval[i] = onerel->rd_att->attrs[keycol - 1]->attbyval; + typlen[i] = onerel->rd_att->attrs[keycol - 1]->attlen; + } + } + /* * Need an EState for evaluation of index expressions and * partial-index predicates. Create it in the per-index context to be *************** compute_index_stats(Relation onerel, dou *** 676,683 **** estate); /* Compute and save index expression values */ ! exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); ! exprnulls = (bool *) palloc(numrows * attr_cnt * sizeof(bool)); numindexrows = 0; tcnt = 0; for (rowno = 0; rowno < numrows; rowno++) --- 879,886 ---- estate); /* Compute and save index expression values */ ! exprvals = (Datum *) palloc(numrows * thisdata->attr_cnt * sizeof(Datum)); ! exprnulls = (bool *) palloc(numrows * thisdata->attr_cnt * sizeof(bool)); numindexrows = 0; tcnt = 0; for (rowno = 0; rowno < numrows; rowno++) *************** compute_index_stats(Relation onerel, dou *** 701,707 **** } numindexrows++; ! if (attr_cnt > 0) { /* * Evaluate the index row to compute expression values. We --- 904,910 ---- } numindexrows++; ! if (stats_cnt > 0) { /* * Evaluate the index row to compute expression values. We *************** compute_index_stats(Relation onerel, dou *** 714,737 **** isnull); /* ! * Save just the columns we care about. We copy the values ! * into ind_context from the estate's per-tuple context. */ ! for (i = 0; i < attr_cnt; i++) { ! VacAttrStats *stats = thisdata->vacattrstats[i]; ! int attnum = stats->attr->attnum; ! ! if (isnull[attnum - 1]) { exprvals[tcnt] = (Datum) 0; exprnulls[tcnt] = true; } else { ! exprvals[tcnt] = datumCopy(values[attnum - 1], ! stats->attrtype->typbyval, ! stats->attrtype->typlen); exprnulls[tcnt] = false; } tcnt++; --- 917,934 ---- isnull); /* ! * We copy the values * into ind_context from the estate's per-tuple context. */ ! for (i = 0; i < thisdata->attr_cnt; i++) { ! if (isnull[i]) { exprvals[tcnt] = (Datum) 0; exprnulls[tcnt] = true; } else { ! exprvals[tcnt] = datumCopy(values[i], typbyval[i], typlen[i]); exprnulls[tcnt] = false; } tcnt++; *************** compute_index_stats(Relation onerel, dou *** 752,779 **** if (numindexrows > 0) { MemoryContextSwitchTo(col_context); ! for (i = 0; i < attr_cnt; i++) { ! VacAttrStats *stats = thisdata->vacattrstats[i]; ! AttributeOpts *aopt = ! get_attribute_options(stats->attr->attrelid, ! stats->attr->attnum); ! stats->exprvals = exprvals + i; ! stats->exprnulls = exprnulls + i; ! stats->rowstride = attr_cnt; ! (*stats->compute_stats) (stats, ! ind_fetch_func, ! numindexrows, ! totalindexrows); ! /* ! * If the n_distinct option is specified, it overrides the ! * above computation. For indices, we always use just ! * n_distinct, not n_distinct_inherited. ! */ ! if (aopt != NULL && aopt->n_distinct != 0.0) ! stats->stadistinct = aopt->n_distinct; MemoryContextResetAndDeleteChildren(col_context); } --- 949,985 ---- if (numindexrows > 0) { MemoryContextSwitchTo(col_context); ! for (i = 0; i < stats_cnt; i++) { ! VacStats *stats = thisdata->vacstats[i]; ! AttributeOpts *aopt; ! int j; ! for (j = 0; j < stats->attnums->dim1; j++) ! { ! if (stats->attnums->dim1 == 1) ! aopt = get_attribute_options(stats->attrs[j]->attrelid, ! stats->attrs[j]->attnum); ! else ! aopt = NULL; ! stats->exprvals = exprvals; ! stats->exprnulls = exprnulls; ! stats->rowstride = thisdata->attr_cnt; ! (*stats->statfuncs[j].compute_func_ptr) (stats, ! j, ! ind_fetch_func, ! numindexrows, ! totalindexrows); ! ! /* ! * If the n_distinct option is specified, it overrides the ! * above computation. For indices, we always use just ! * n_distinct, not n_distinct_inherited. ! */ ! if (aopt != NULL && aopt->n_distinct != 0.0) ! stats->stadistinct[j] = aopt->n_distinct; ! } MemoryContextResetAndDeleteChildren(col_context); } *************** compute_index_stats(Relation onerel, dou *** 792,829 **** } /* ! * examine_attribute -- pre-analysis of a single column * * Determine whether the column is analyzable; if so, create and initialize ! * a VacAttrStats struct for it. If not, return NULL. * * If index_expr isn't NULL, then we're trying to analyze an expression index, * 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; ! /* Never analyze dropped columns */ ! if (attr->attisdropped) ! return NULL; /* Don't analyze column if user has specified not to */ ! if (attr->attstattarget == 0) return NULL; /* ! * Create the VacAttrStats struct. Note that we only have a copy of the * fixed fields of the pg_attribute tuple. */ ! stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats)); ! stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE); ! memcpy(stats->attr, attr, ATTRIBUTE_FIXED_PART_SIZE); /* * When analyzing an expression index, believe the expression tree's type --- 998,1074 ---- } /* ! * statistics_target -- returns pg_statistic.statarget ! */ ! static int4 ! statistics_target(Oid relid, int2vector *attnums, bool inherited) ! { ! HeapTuple tuple; ! int4 statarget = -1; /* default */ ! ! tuple = SearchSysCache3(STATRELATTINH, ! ObjectIdGetDatum(relid), ! PointerGetDatum(attnums), ! BoolGetDatum(inherited)); ! if (HeapTupleIsValid(tuple)) ! { ! Form_pg_statistic stats = (Form_pg_statistic) GETSTRUCT(tuple); ! ! statarget = stats->statarget; ! ! ReleaseSysCache(tuple); ! } ! ! return statarget; ! } ! ! ! /* ! * examine_attribute -- pre-analysis of a single column or a set of columns * * Determine whether the column is analyzable; if so, create and initialize ! * a VacStats struct for it. If not, return NULL. * * If index_expr isn't NULL, then we're trying to analyze an expression index, * and index_expr is the expression tree representing the column's data. */ ! static VacStats * ! examine_attribute(Relation onerel, int2vector *attnums, bool inh, Node **index_exprs) { HeapTuple typtuple; ! int4 statarget; ! VacStats *stats; ! int i, j; bool ok; ! for (i = 0; i < attnums->dim1; i++) ! { ! Form_pg_attribute attr = onerel->rd_att->attrs[attnums->values[i] - 1]; ! ! /* Never analyze dropped columns */ ! if (attr->attisdropped) ! return NULL; ! } /* Don't analyze column if user has specified not to */ ! statarget = statistics_target(onerel->rd_id, attnums, inh); ! if (statarget == 0) return NULL; /* ! * Create the VacStats struct. Note that we only have a copy of the * fixed fields of the pg_attribute tuple. */ ! stats = (VacStats *) palloc0(sizeof(VacStats)); ! for (i = 0; i < attnums->dim1; i++) ! { ! Form_pg_attribute attr = onerel->rd_att->attrs[attnums->values[i] - 1]; ! stats->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE); ! memcpy(stats->attrs[i], attr, ATTRIBUTE_FIXED_PART_SIZE); ! } ! ! stats->attnums = attnums; ! stats->statarget = statarget; /* * When analyzing an expression index, believe the expression tree's type *************** examine_attribute(Relation onerel, int a *** 834,887 **** * not a problem.) It's not clear whether anyone will care about the * typmod, but we store that too just in case. */ ! if (index_expr) ! { ! stats->attrtypid = exprType(index_expr); ! stats->attrtypmod = exprTypmod(index_expr); ! } ! else { ! stats->attrtypid = attr->atttypid; ! stats->attrtypmod = attr->atttypmod; } - typtuple = SearchSysCacheCopy1(TYPEOID, - ObjectIdGetDatum(stats->attrtypid)); - if (!HeapTupleIsValid(typtuple)) - elog(ERROR, "cache lookup failed for type %u", stats->attrtypid); - stats->attrtype = (Form_pg_type) GETSTRUCT(typtuple); stats->anl_context = anl_context; - stats->tupattnum = attnum; /* * The fields describing the stats->stavalues[n] element types default to * the type of the data being analyzed, but the type-specific typanalyze * function can change them if it wants to store something else. */ ! for (i = 0; i < STATISTIC_NUM_SLOTS; i++) { ! stats->statypid[i] = stats->attrtypid; ! stats->statyplen[i] = stats->attrtype->typlen; ! stats->statypbyval[i] = stats->attrtype->typbyval; ! stats->statypalign[i] = stats->attrtype->typalign; } /* ! * Call the type-specific typanalyze function. If none is specified, use ! * std_typanalyze(). */ ! if (OidIsValid(stats->attrtype->typanalyze)) ! ok = DatumGetBool(OidFunctionCall1(stats->attrtype->typanalyze, ! PointerGetDatum(stats))); ! else ! ok = std_typanalyze(stats); ! ! if (!ok || stats->compute_stats == NULL || stats->minrows <= 0) { ! heap_freetuple(typtuple); ! pfree(stats->attr); ! pfree(stats); ! return NULL; } return stats; --- 1079,1164 ---- * not a problem.) It's not clear whether anyone will care about the * typmod, but we store that too just in case. */ ! ! for (i = 0; i < attnums->dim1; i++) { ! if (index_exprs && index_exprs[i]) ! { ! stats->attrtypids[i] = exprType(index_exprs[i]); ! stats->attrtypmods[i] = exprTypmod(index_exprs[i]); ! } ! else ! { ! stats->attrtypids[i] = stats->attrs[i]->atttypid; ! stats->attrtypmods[i] = stats->attrs[i]->atttypmod; ! } ! ! typtuple = SearchSysCacheCopy1(TYPEOID, ! ObjectIdGetDatum(stats->attrtypids[i])); ! if (!HeapTupleIsValid(typtuple)) ! elog(ERROR, "cache lookup failed for type %u", stats->attrtypids[i]); ! stats->attrtypes[i] = (Form_pg_type) GETSTRUCT(typtuple); } stats->anl_context = anl_context; /* * The fields describing the stats->stavalues[n] element types default to * the type of the data being analyzed, but the type-specific typanalyze * function can change them if it wants to store something else. */ ! for (i = 0; i < attnums->dim1; i++) { ! for (j = 0; j < STATISTIC_NUM_SLOTS; j++) ! { ! stats->statypid[i][j] = stats->attrtypids[i]; ! stats->statyplen[i][j] = stats->attrtypes[i]->typlen; ! stats->statypbyval[i][j] = stats->attrtypes[i]->typbyval; ! stats->statypalign[i][j] = stats->attrtypes[i]->typalign; ! } ! ! /* ! * Call the type-specific typanalyze function. If none is specified, use ! * std_typanalyze(). ! */ ! if (OidIsValid(stats->attrtypes[i]->typanalyze)) ! ok = DatumGetBool(OidFunctionCall2(stats->attrtypes[i]->typanalyze, ! PointerGetDatum(stats), ! Int32GetDatum(i))); ! else ! ok = std_typanalyze(stats, i); ! ! if (!ok || ! stats->statfuncs[i].compute_func_ptr == NULL || ! /* intentionally don't check stats->statfuncs[i].findval_func_ptr == NULL */ ! stats->minrows <= 0) ! { ! for (; i >= 0; i--) ! { ! heap_freetuple((HeapTuple) stats->attrtypes[i]); ! pfree(stats->attrs[i]); ! } ! pfree(stats); ! ! return NULL; ! } } /* ! * If we have a cross-column statistics, compute the cube size for ! * the histogram so pg_statistic.statarget really means the number of ! * cells in the multi-dimensional cube. */ ! if (stats->attnums->dim1 > 1) { ! double x, y, n; ! ! y = stats->statarget; ! n = stats->attnums->dim1; ! x = pow(M_E, (log(y) / n)); ! ! /* On some systems, ceil() returns surprising result. */ ! stats->statarget = trunc(x + 0.5); } return stats; *************** acquire_inherited_sample_rows(Relation o *** 1555,1561 **** * by taking a self-exclusive lock on the relation in analyze_rel(). */ static void ! update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats) { Relation sd; int attno; --- 1832,1838 ---- * by taking a self-exclusive lock on the relation in analyze_rel(). */ static void ! update_attstats(Oid relid, bool inh, int natts, VacStats **vacstats) { Relation sd; int attno; *************** update_attstats(Oid relid, bool inh, int *** 1567,1573 **** for (attno = 0; attno < natts; attno++) { ! VacAttrStats *stats = vacattrstats[attno]; HeapTuple stup, oldtup; int i, --- 1844,1850 ---- for (attno = 0; attno < natts; attno++) { ! VacStats *stats = vacstats[attno]; HeapTuple stup, oldtup; int i, *************** update_attstats(Oid relid, bool inh, int *** 1590,1615 **** replaces[i] = true; } 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); i = Anum_pg_statistic_stakind1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! values[i++] = Int16GetDatum(stats->stakind[k]); /* stakindN */ } i = Anum_pg_statistic_staop1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! values[i++] = ObjectIdGetDatum(stats->staop[k]); /* staopN */ } i = Anum_pg_statistic_stanumbers1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! int nnum = stats->numnumbers[k]; if (nnum > 0) { --- 1867,1901 ---- replaces[i] = true; } + /* + * We use statarget = -1 here, so if the pg_statistic entry is new, + * we use the default_statistics_target for it. If there's a pg_statistic + * row for this statistics, we will set the "replaces" flag to false. + */ + values[Anum_pg_statistic_statarget - 1] = Int32GetDatum(-1); values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(relid); values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inh); ! values[Anum_pg_statistic_stavalid - 1] = BoolGetDatum(true); ! values[Anum_pg_statistic_stanullfrac - 1] = Float4GetDatum(stats->stanullfrac[0]); ! values[Anum_pg_statistic_stawidth - 1] = Int32GetDatum(stats->stawidth[0]); ! values[Anum_pg_statistic_stadistinct - 1] = Float4GetDatum(stats->stadistinct[0]); i = Anum_pg_statistic_stakind1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! values[i++] = Int16GetDatum(stats->stakind[0][k]); /* stakindN */ } i = Anum_pg_statistic_staop1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! values[i++] = ObjectIdGetDatum(stats->staop[0][k]); /* staopN */ } + + values[Anum_pg_statistic_staattnums - 1] = PointerGetDatum(stats->attnums); /* staattnums */ + i = Anum_pg_statistic_stanumbers1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! int nnum = stats->numnumbers[0][k]; if (nnum > 0) { *************** update_attstats(Oid relid, bool inh, int *** 1617,1623 **** ArrayType *arry; for (n = 0; n < nnum; n++) ! numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]); /* XXX knows more than it should about type float4: */ arry = construct_array(numdatums, nnum, FLOAT4OID, --- 1903,1909 ---- ArrayType *arry; for (n = 0; n < nnum; n++) ! numdatums[n] = Float4GetDatum(stats->stanumbers[0][k][n]); /* XXX knows more than it should about type float4: */ arry = construct_array(numdatums, nnum, FLOAT4OID, *************** update_attstats(Oid relid, bool inh, int *** 1633,1648 **** i = Anum_pg_statistic_stavalues1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! if (stats->numvalues[k] > 0) { ArrayType *arry; ! arry = construct_array(stats->stavalues[k], ! stats->numvalues[k], ! stats->statypid[k], ! stats->statyplen[k], ! stats->statypbyval[k], ! stats->statypalign[k]); values[i++] = PointerGetDatum(arry); /* stavaluesN */ } else --- 1919,1947 ---- i = Anum_pg_statistic_stavalues1 - 1; for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { ! int idx1, idx2; ! ! if (stats->attnums->dim1 > 1 && k < stats->attnums->dim1) ! { ! idx1 = k; ! idx2 = 0; ! } ! else ! { ! idx1 = 0; ! idx2 = k; ! } ! ! if (stats->numvalues[idx1][idx2] > 0) { ArrayType *arry; ! arry = construct_array(stats->stavalues[idx1][idx2], ! stats->numvalues[idx1][idx2], ! stats->statypid[idx1][idx2], ! stats->statyplen[idx1][idx2], ! stats->statypbyval[idx1][idx2], ! stats->statypalign[idx1][idx2]); values[i++] = PointerGetDatum(arry); /* stavaluesN */ } else *************** update_attstats(Oid relid, bool inh, int *** 1655,1665 **** /* Is there already a pg_statistic tuple for this attribute? */ oldtup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(relid), ! Int16GetDatum(stats->attr->attnum), BoolGetDatum(inh)); if (HeapTupleIsValid(oldtup)) { /* Yes, replace it */ stup = heap_modify_tuple(oldtup, RelationGetDescr(sd), --- 1954,1967 ---- /* Is there already a pg_statistic tuple for this attribute? */ oldtup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(relid), ! PointerGetDatum(stats->attnums), BoolGetDatum(inh)); if (HeapTupleIsValid(oldtup)) { + /* Skip replacing the statarget value, so we remember its value. */ + replaces[Anum_pg_statistic_statarget - 1] = false; + /* Yes, replace it */ stup = heap_modify_tuple(oldtup, RelationGetDescr(sd), *************** update_attstats(Oid relid, bool inh, int *** 1692,1704 **** * and the actual storage of the sample data. */ static Datum ! std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull) { - int attnum = stats->tupattnum; HeapTuple tuple = stats->rows[rownum]; TupleDesc tupDesc = stats->tupDesc; ! return heap_getattr(tuple, attnum, tupDesc, isNull); } /* --- 1994,2005 ---- * and the actual storage of the sample data. */ static Datum ! std_fetch_func(VacStatsP stats, int rownum, AttrNumber tupattnum, bool *isNull) { HeapTuple tuple = stats->rows[rownum]; TupleDesc tupDesc = stats->tupDesc; ! return heap_getattr(tuple, tupattnum, tupDesc, isNull); } /* *************** std_fetch_func(VacAttrStatsP stats, int *** 1708,1719 **** * just in Datum arrays. */ static Datum ! ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull) { int i; ! /* exprvals and exprnulls are already offset for proper column */ ! i = rownum * stats->rowstride; *isNull = stats->exprnulls[i]; return stats->exprvals[i]; } --- 2009,2019 ---- * just in Datum arrays. */ static Datum ! ind_fetch_func(VacStatsP stats, int rownum, AttrNumber tupattnum, bool *isNull) { int i; ! i = rownum * stats->rowstride + tupattnum - 1; *isNull = stats->exprnulls[i]; return stats->exprvals[i]; } *************** typedef struct *** 1772,1783 **** int *tupnoLink; } CompareScalarsContext; ! static void compute_minimal_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows); ! static void compute_scalar_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows); --- 2072,2090 ---- int *tupnoLink; } CompareScalarsContext; + typedef struct + { + FmgrInfo cmpFn; + int cmpFlags; + } CompareDatumsContext; ! static void compute_minimal_stats(VacStatsP stats, ! int index, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows); ! static void compute_scalar_stats(VacStatsP stats, ! int index, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows); *************** static int compare_mcvs(const void *a, c *** 1789,1808 **** * std_typanalyze -- the default type-specific typanalyze function */ 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, false, false, false, <opr, &eqopr, NULL, NULL); --- 2096,2114 ---- * std_typanalyze -- the default type-specific typanalyze function */ static bool ! std_typanalyze(VacStats *stats, int index) { 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->attrtypids[index], false, false, false, <opr, &eqopr, NULL, NULL); *************** std_typanalyze(VacAttrStats *stats) *** 1816,1822 **** mystats->eqopr = eqopr; mystats->eqfunc = get_opcode(eqopr); mystats->ltopr = ltopr; ! stats->extra_data = mystats; /* * Determine which standard statistics algorithm to use --- 2122,2128 ---- mystats->eqopr = eqopr; mystats->eqfunc = get_opcode(eqopr); mystats->ltopr = ltopr; ! stats->extra_data[index] = mystats; /* * Determine which standard statistics algorithm to use *************** std_typanalyze(VacAttrStats *stats) *** 1824,1830 **** if (OidIsValid(ltopr)) { /* Seems to be a scalar datatype */ ! stats->compute_stats = compute_scalar_stats; /*-------------------- * The following choice of minrows is based on the paper * "Random sampling for histogram construction: how much is enough?" --- 2130,2137 ---- if (OidIsValid(ltopr)) { /* Seems to be a scalar datatype */ ! stats->statfuncs[index].compute_func_ptr = compute_scalar_stats; ! stats->statfuncs[index].findval_func_ptr = ordered_findval; /*-------------------- * The following choice of minrows is based on the paper * "Random sampling for histogram construction: how much is enough?" *************** 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; --- 2151,2165 ---- * know it at this point. *-------------------- */ ! stats->minrows = 300 * stats->statarget; } else { /* Can't do much but the minimal stuff */ ! stats->statfuncs[index].compute_func_ptr = compute_minimal_stats; ! stats->statfuncs[index].findval_func_ptr = unordered_findval; /* Might as well use the same minrows as above */ ! stats->minrows = 300 * stats->statarget; } return true; *************** std_typanalyze(VacAttrStats *stats) *** 1873,1879 **** * depend mainly on the length of the list we are willing to keep. */ static void ! compute_minimal_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) --- 2181,2188 ---- * depend mainly on the length of the list we are willing to keep. */ static void ! compute_minimal_stats(VacStatsP stats, ! int index, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) *************** compute_minimal_stats(VacAttrStatsP stat *** 1883,1892 **** int nonnull_cnt = 0; int toowide_cnt = 0; double total_width = 0; ! bool is_varlena = (!stats->attrtype->typbyval && ! stats->attrtype->typlen == -1); ! bool is_varwidth = (!stats->attrtype->typbyval && ! stats->attrtype->typlen < 0); FmgrInfo f_cmpeq; typedef struct { --- 2192,2201 ---- int nonnull_cnt = 0; int toowide_cnt = 0; double total_width = 0; ! bool is_varlena = (!stats->attrtypes[index]->typbyval && ! stats->attrtypes[index]->typlen == -1); ! bool is_varwidth = (!stats->attrtypes[index]->typbyval && ! stats->attrtypes[index]->typlen < 0); FmgrInfo f_cmpeq; typedef struct { *************** compute_minimal_stats(VacAttrStatsP stat *** 1896,1903 **** TrackItem *track; int track_cnt, track_max; ! int num_mcv = stats->attr->attstattarget; ! StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data; /* * We track up to 2*n values for an n-element MCV list; but at least 10 --- 2205,2212 ---- TrackItem *track; int track_cnt, track_max; ! int num_mcv = stats->statarget; ! StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data[index]; /* * We track up to 2*n values for an n-element MCV list; but at least 10 *************** compute_minimal_stats(VacAttrStatsP stat *** 1920,1926 **** vacuum_delay_point(); ! value = fetchfunc(stats, i, &isnull); /* Check for null/nonnull */ if (isnull) --- 2229,2235 ---- vacuum_delay_point(); ! value = fetchfunc(stats, i, stats->attnums->values[index], &isnull); /* Check for null/nonnull */ if (isnull) *************** compute_minimal_stats(VacAttrStatsP stat *** 2017,2027 **** stats->stats_valid = true; /* Do the simple null-frac and width stats */ ! stats->stanullfrac = (double) null_cnt / (double) samplerows; if (is_varwidth) ! stats->stawidth = total_width / (double) nonnull_cnt; else ! stats->stawidth = stats->attrtype->typlen; /* Count the number of values we found multiple times */ summultiple = 0; --- 2326,2336 ---- stats->stats_valid = true; /* Do the simple null-frac and width stats */ ! stats->stanullfrac[index] = (double) null_cnt / (double) samplerows; if (is_varwidth) ! stats->stawidth[index] = total_width / (double) nonnull_cnt; else ! stats->stawidth[index] = stats->attrtypes[index]->typlen; /* Count the number of values we found multiple times */ summultiple = 0; *************** compute_minimal_stats(VacAttrStatsP stat *** 2035,2041 **** if (nmultiple == 0) { /* If we found no repeated values, assume it's a unique column */ ! stats->stadistinct = -1.0; } else if (track_cnt < track_max && toowide_cnt == 0 && nmultiple == track_cnt) --- 2344,2350 ---- if (nmultiple == 0) { /* If we found no repeated values, assume it's a unique column */ ! stats->stadistinct[index] = -1.0; } else if (track_cnt < track_max && toowide_cnt == 0 && nmultiple == track_cnt) *************** compute_minimal_stats(VacAttrStatsP stat *** 2045,2051 **** * value appeared more than once. Assume the column has just * these values. */ ! stats->stadistinct = track_cnt; } else { --- 2354,2360 ---- * value appeared more than once. Assume the column has just * these values. */ ! stats->stadistinct[index] = track_cnt; } else { *************** compute_minimal_stats(VacAttrStatsP stat *** 2084,2090 **** stadistinct = (double) d; if (stadistinct > totalrows) stadistinct = totalrows; ! stats->stadistinct = floor(stadistinct + 0.5); } /* --- 2393,2399 ---- stadistinct = (double) d; if (stadistinct > totalrows) stadistinct = totalrows; ! stats->stadistinct[index] = floor(stadistinct + 0.5); } /* *************** compute_minimal_stats(VacAttrStatsP stat *** 2093,2100 **** * stadistinct should scale with the row count rather than be a fixed * value. */ ! if (stats->stadistinct > 0.1 * totalrows) ! stats->stadistinct = -(stats->stadistinct / totalrows); /* * Decide how many values are worth storing as most-common values. If --- 2402,2409 ---- * stadistinct should scale with the row count rather than be a fixed * value. */ ! if (stats->stadistinct[index] > 0.1 * totalrows) ! stats->stadistinct[index] = -(stats->stadistinct[index] / totalrows); /* * Decide how many values are worth storing as most-common values. If *************** compute_minimal_stats(VacAttrStatsP stat *** 2114,2120 **** } else { ! double ndistinct = stats->stadistinct; double avgcount, mincount; --- 2423,2429 ---- } else { ! double ndistinct = stats->stadistinct[index]; double avgcount, mincount; *************** compute_minimal_stats(VacAttrStatsP stat *** 2152,2169 **** for (i = 0; i < num_mcv; i++) { mcv_values[i] = datumCopy(track[i].value, ! stats->attrtype->typbyval, ! stats->attrtype->typlen); mcv_freqs[i] = (double) track[i].count / (double) samplerows; } MemoryContextSwitchTo(old_context); ! stats->stakind[0] = STATISTIC_KIND_MCV; ! stats->staop[0] = mystats->eqopr; ! stats->stanumbers[0] = mcv_freqs; ! stats->numnumbers[0] = num_mcv; ! stats->stavalues[0] = mcv_values; ! stats->numvalues[0] = num_mcv; /* * Accept the defaults for stats->statypid and others. They have --- 2461,2478 ---- for (i = 0; i < num_mcv; i++) { mcv_values[i] = datumCopy(track[i].value, ! stats->attrtypes[index]->typbyval, ! stats->attrtypes[index]->typlen); mcv_freqs[i] = (double) track[i].count / (double) samplerows; } MemoryContextSwitchTo(old_context); ! stats->stakind[index][0] = STATISTIC_KIND_MCV; ! stats->staop[index][0] = mystats->eqopr; ! stats->stanumbers[index][0] = mcv_freqs; ! stats->numnumbers[index][0] = num_mcv; ! stats->stavalues[index][0] = mcv_values; ! stats->numvalues[index][0] = num_mcv; /* * Accept the defaults for stats->statypid and others. They have *************** compute_minimal_stats(VacAttrStatsP stat *** 2175,2186 **** { /* We found only nulls; assume the column is entirely null */ stats->stats_valid = true; ! stats->stanullfrac = 1.0; if (is_varwidth) ! stats->stawidth = 0; /* "unknown" */ else ! stats->stawidth = stats->attrtype->typlen; ! stats->stadistinct = 0.0; /* "unknown" */ } /* We don't need to bother cleaning up any of our temporary palloc's */ --- 2484,2495 ---- { /* We found only nulls; assume the column is entirely null */ stats->stats_valid = true; ! stats->stanullfrac[index] = 1.0; if (is_varwidth) ! stats->stawidth[index] = 0; /* "unknown" */ else ! stats->stawidth[index] = stats->attrtypes[index]->typlen; ! stats->stadistinct[index] = 0.0; /* "unknown" */ } /* We don't need to bother cleaning up any of our temporary palloc's */ *************** compute_minimal_stats(VacAttrStatsP stat *** 2200,2206 **** * data values into order. */ static void ! compute_scalar_stats(VacAttrStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) --- 2509,2516 ---- * data values into order. */ static void ! compute_scalar_stats(VacStatsP stats, ! int index, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) *************** compute_scalar_stats(VacAttrStatsP stats *** 2210,2219 **** int nonnull_cnt = 0; int toowide_cnt = 0; double total_width = 0; ! bool is_varlena = (!stats->attrtype->typbyval && ! stats->attrtype->typlen == -1); ! bool is_varwidth = (!stats->attrtype->typbyval && ! stats->attrtype->typlen < 0); double corr_xysum; Oid cmpFn; int cmpFlags; --- 2520,2529 ---- int nonnull_cnt = 0; int toowide_cnt = 0; double total_width = 0; ! bool is_varlena = (!stats->attrtypes[index]->typbyval && ! stats->attrtypes[index]->typlen == -1); ! bool is_varwidth = (!stats->attrtypes[index]->typbyval && ! stats->attrtypes[index]->typlen < 0); double corr_xysum; Oid cmpFn; int cmpFlags; *************** compute_scalar_stats(VacAttrStatsP stats *** 2223,2231 **** 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)); tupnoLink = (int *) palloc(samplerows * sizeof(int)); --- 2533,2541 ---- int *tupnoLink; ScalarMCVItem *track; int track_cnt = 0; ! int num_mcv = stats->statarget; ! int num_bins = stats->statarget; ! StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data[index]; values = (ScalarItem *) palloc(samplerows * sizeof(ScalarItem)); tupnoLink = (int *) palloc(samplerows * sizeof(int)); *************** compute_scalar_stats(VacAttrStatsP stats *** 2242,2248 **** vacuum_delay_point(); ! value = fetchfunc(stats, i, &isnull); /* Check for null/nonnull */ if (isnull) --- 2552,2558 ---- vacuum_delay_point(); ! value = fetchfunc(stats, i, stats->attnums->values[index], &isnull); /* Check for null/nonnull */ if (isnull) *************** compute_scalar_stats(VacAttrStatsP stats *** 2372,2387 **** stats->stats_valid = true; /* Do the simple null-frac and width stats */ ! stats->stanullfrac = (double) null_cnt / (double) samplerows; if (is_varwidth) ! stats->stawidth = total_width / (double) nonnull_cnt; else ! stats->stawidth = stats->attrtype->typlen; if (nmultiple == 0) { /* If we found no repeated values, assume it's a unique column */ ! stats->stadistinct = -1.0; } else if (toowide_cnt == 0 && nmultiple == ndistinct) { --- 2682,2697 ---- stats->stats_valid = true; /* Do the simple null-frac and width stats */ ! stats->stanullfrac[index] = (double) null_cnt / (double) samplerows; if (is_varwidth) ! stats->stawidth[index] = total_width / (double) nonnull_cnt; else ! stats->stawidth[index] = stats->attrtypes[index]->typlen; if (nmultiple == 0) { /* If we found no repeated values, assume it's a unique column */ ! stats->stadistinct[index] = -1.0; } else if (toowide_cnt == 0 && nmultiple == ndistinct) { *************** compute_scalar_stats(VacAttrStatsP stats *** 2389,2395 **** * Every value in the sample appeared more than once. Assume the * column has just these values. */ ! stats->stadistinct = ndistinct; } else { --- 2699,2705 ---- * Every value in the sample appeared more than once. Assume the * column has just these values. */ ! stats->stadistinct[index] = ndistinct; } else { *************** compute_scalar_stats(VacAttrStatsP stats *** 2424,2430 **** stadistinct = (double) d; if (stadistinct > totalrows) stadistinct = totalrows; ! stats->stadistinct = floor(stadistinct + 0.5); } /* --- 2734,2740 ---- stadistinct = (double) d; if (stadistinct > totalrows) stadistinct = totalrows; ! stats->stadistinct[index] = floor(stadistinct + 0.5); } /* *************** compute_scalar_stats(VacAttrStatsP stats *** 2433,2440 **** * stadistinct should scale with the row count rather than be a fixed * value. */ ! if (stats->stadistinct > 0.1 * totalrows) ! stats->stadistinct = -(stats->stadistinct / totalrows); /* * Decide how many values are worth storing as most-common values. If --- 2743,2750 ---- * stadistinct should scale with the row count rather than be a fixed * value. */ ! if (stats->stadistinct[index] > 0.1 * totalrows) ! stats->stadistinct[index] = -(stats->stadistinct[index] / totalrows); /* * Decide how many values are worth storing as most-common values. If *************** compute_scalar_stats(VacAttrStatsP stats *** 2451,2457 **** * but we prefer to treat such values as MCVs if at all possible.) */ if (track_cnt == ndistinct && toowide_cnt == 0 && ! stats->stadistinct > 0 && track_cnt <= num_mcv) { /* Track list includes all values seen, and all will fit */ --- 2761,2767 ---- * but we prefer to treat such values as MCVs if at all possible.) */ if (track_cnt == ndistinct && toowide_cnt == 0 && ! stats->stadistinct[index] > 0 && track_cnt <= num_mcv) { /* Track list includes all values seen, and all will fit */ *************** compute_scalar_stats(VacAttrStatsP stats *** 2459,2465 **** } else { ! double ndistinct = stats->stadistinct; double avgcount, mincount, maxmincount; --- 2769,2775 ---- } else { ! double ndistinct = stats->stadistinct[index]; double avgcount, mincount, maxmincount; *************** compute_scalar_stats(VacAttrStatsP stats *** 2502,2519 **** for (i = 0; i < num_mcv; i++) { mcv_values[i] = datumCopy(values[track[i].first].value, ! stats->attrtype->typbyval, ! stats->attrtype->typlen); mcv_freqs[i] = (double) track[i].count / (double) samplerows; } MemoryContextSwitchTo(old_context); ! stats->stakind[slot_idx] = STATISTIC_KIND_MCV; ! stats->staop[slot_idx] = mystats->eqopr; ! stats->stanumbers[slot_idx] = mcv_freqs; ! stats->numnumbers[slot_idx] = num_mcv; ! stats->stavalues[slot_idx] = mcv_values; ! stats->numvalues[slot_idx] = num_mcv; /* * Accept the defaults for stats->statypid and others. They have --- 2812,2829 ---- for (i = 0; i < num_mcv; i++) { mcv_values[i] = datumCopy(values[track[i].first].value, ! stats->attrtypes[index]->typbyval, ! stats->attrtypes[index]->typlen); mcv_freqs[i] = (double) track[i].count / (double) samplerows; } MemoryContextSwitchTo(old_context); ! stats->stakind[index][slot_idx] = STATISTIC_KIND_MCV; ! stats->staop[index][slot_idx] = mystats->eqopr; ! stats->stanumbers[index][slot_idx] = mcv_freqs; ! stats->numnumbers[index][slot_idx] = num_mcv; ! stats->stavalues[index][slot_idx] = mcv_values; ! stats->numvalues[index][slot_idx] = num_mcv; /* * Accept the defaults for stats->statypid and others. They have *************** compute_scalar_stats(VacAttrStatsP stats *** 2609,2616 **** for (i = 0; i < num_hist; i++) { hist_values[i] = datumCopy(values[pos].value, ! stats->attrtype->typbyval, ! stats->attrtype->typlen); pos += delta; posfrac += deltafrac; if (posfrac >= (num_hist - 1)) --- 2919,2926 ---- for (i = 0; i < num_hist; i++) { hist_values[i] = datumCopy(values[pos].value, ! stats->attrtypes[index]->typbyval, ! stats->attrtypes[index]->typlen); pos += delta; posfrac += deltafrac; if (posfrac >= (num_hist - 1)) *************** compute_scalar_stats(VacAttrStatsP stats *** 2623,2632 **** MemoryContextSwitchTo(old_context); ! stats->stakind[slot_idx] = STATISTIC_KIND_HISTOGRAM; ! stats->staop[slot_idx] = mystats->ltopr; ! stats->stavalues[slot_idx] = hist_values; ! stats->numvalues[slot_idx] = num_hist; /* * Accept the defaults for stats->statypid and others. They have --- 2933,2942 ---- MemoryContextSwitchTo(old_context); ! stats->stakind[index][slot_idx] = STATISTIC_KIND_HISTOGRAM; ! stats->staop[index][slot_idx] = mystats->ltopr; ! stats->stavalues[index][slot_idx] = hist_values; ! stats->numvalues[index][slot_idx] = num_hist; /* * Accept the defaults for stats->statypid and others. They have *************** compute_scalar_stats(VacAttrStatsP stats *** 2666,2675 **** corrs[0] = (values_cnt * corr_xysum - corr_xsum * corr_xsum) / (values_cnt * corr_x2sum - corr_xsum * corr_xsum); ! stats->stakind[slot_idx] = STATISTIC_KIND_CORRELATION; ! stats->staop[slot_idx] = mystats->ltopr; ! stats->stanumbers[slot_idx] = corrs; ! stats->numnumbers[slot_idx] = 1; slot_idx++; } } --- 2976,2985 ---- corrs[0] = (values_cnt * corr_xysum - corr_xsum * corr_xsum) / (values_cnt * corr_x2sum - corr_xsum * corr_xsum); ! stats->stakind[index][slot_idx] = STATISTIC_KIND_CORRELATION; ! stats->staop[index][slot_idx] = mystats->ltopr; ! stats->stanumbers[index][slot_idx] = corrs; ! stats->numnumbers[index][slot_idx] = 1; slot_idx++; } } *************** compute_scalar_stats(VacAttrStatsP stats *** 2677,2688 **** { /* We found only nulls; assume the column is entirely null */ stats->stats_valid = true; ! stats->stanullfrac = 1.0; if (is_varwidth) ! stats->stawidth = 0; /* "unknown" */ else ! stats->stawidth = stats->attrtype->typlen; ! stats->stadistinct = 0.0; /* "unknown" */ } /* We don't need to bother cleaning up any of our temporary palloc's */ --- 2987,2998 ---- { /* We found only nulls; assume the column is entirely null */ stats->stats_valid = true; ! stats->stanullfrac[index] = 1.0; if (is_varwidth) ! stats->stawidth[index] = 0; /* "unknown" */ else ! stats->stawidth[index] = stats->attrtypes[index]->typlen; ! stats->stadistinct[index] = 0.0; /* "unknown" */ } /* We don't need to bother cleaning up any of our temporary palloc's */ *************** compare_scalars(const void *a, const voi *** 2729,2734 **** --- 3039,3243 ---- } /* + * qsort_arg comparator for cross column statistics + * The dimension axes need to be properly ordered. + */ + static int + compare_datums(const void *a, const void *b, void *arg) + { + Datum da = *((Datum *) a); + Datum db = *((Datum *) b); + CompareDatumsContext *cxt = (CompareDatumsContext *) arg; + + return ApplySortFunction(&(cxt->cmpFn), cxt->cmpFlags, + DEFAULT_COLLATION_OID, + da, false, db, false); + } + + /* + * Find the index in stavalues for a Datum with ordering operators. + * Because it's ordered, we can use the ordering operator for binary searching. + * Returns: 0 for a NULL value, >= 1 for a valid index. + */ + static int + ordered_findval(VacStatsP stats, int rownum, int index, AnalyzeAttrFetchFunc fetchfunc, void *arg) + { + Datum value; + bool isnull; + int idx_lo, idx_hi, idx_mid; + int32 compare; + CompareDatumsContext *ctx = (CompareDatumsContext *) arg; + + value = fetchfunc(stats, rownum, stats->attnums->values[index], &isnull); + if (isnull) + return 0; + + /* Clamp values smaller than the first bucket to the first bucket */ + idx_lo = 0; + compare = ApplySortFunction(&(ctx->cmpFn), ctx->cmpFlags, + DEFAULT_COLLATION_OID, + value, false, stats->stavalues[index][0][idx_lo], false); + if (compare < 0) + return idx_lo + 1; + + /* + * Buckets have values greater or equal than the bucket's stavalue + * but less than the next bucket's stavalue. There's no next bucket + * for the last one, so check it here. + */ + idx_hi = stats->numvalues[index][0] - 1; + compare = ApplySortFunction(&(ctx->cmpFn), ctx->cmpFlags, + DEFAULT_COLLATION_OID, + value, false, stats->stavalues[index][0][idx_hi], false); + if (compare >= 0) + return idx_hi + 1; + + idx_mid = (idx_lo + idx_hi) / 2; + + while (idx_lo < idx_hi && idx_mid > idx_lo) + { + compare = ApplySortFunction(&(ctx->cmpFn), ctx->cmpFlags, + DEFAULT_COLLATION_OID, + value, false, stats->stavalues[index][0][idx_mid], false); + if (compare < 0) + idx_hi = idx_mid - 1; + else /* if (compare >= 0) */ + idx_lo = idx_mid; + + idx_mid = (idx_lo + idx_hi) / 2; + } + + return idx_lo + 1; + } + + /* + * Find the index in stavalues for a Datum with equality operator only. + * Byte the bullet and loop through the stavalues array. + * Returns: 0 for NULL value, >= 1 for a valid index and -1 for a Datum + * that isn't in stavalues (ignored value). + */ + static int + unordered_findval(VacStatsP stats, int rownum, int index, AnalyzeAttrFetchFunc fetchfunc, void *arg) + { + CompareDatumsContext *ctx = (CompareDatumsContext *) arg; + Datum value; + bool isnull; + int i; + + value = fetchfunc(stats, rownum, stats->attnums->values[index], &isnull); + if (isnull) + return 0; + + for (i = 0; i < stats->numvalues[index][0]; i++) + { + if (DatumGetBool(FunctionCall2Coll(&(ctx->cmpFn), + DEFAULT_COLLATION_OID, + value, stats->stavalues[index][0][i]))) + return i + 1; + } + + return -1; + } + + /* + * Compute a cross-column statistics. + */ + static void + compute_cross_column_stats(VacStatsP stats, AnalyzeAttrFetchFunc fetchfunc, int numrows) + { + int i, j, numvalues; + int dimstride[STATISTIC_NUM_SLOTS]; + MemoryContext old_context; + CompareDatumsContext ctx[STATISTIC_NUM_SLOTS]; + float4 *hist; + + /* Don't touch single column statistics. */ + if (stats->attnums->dim1 == 1) + return; + + /* + * Don't compute cross-column statistics if there is no way to find + * a Datum's index in the stavalues[] array + */ + for (i = 0; i < stats->attnums->dim1; i++) + if (stats->statfuncs[i].findval_func_ptr == NULL) + return; + + /* We need ordered stavalues arrays whenever possible. */ + for (i = 0; i < stats->attnums->dim1; i++) + { + StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data[i]; + + if (OidIsValid(mystats->ltopr)) + { + Oid cmpFn; + + SelectSortFunction(mystats->ltopr, false, &cmpFn, &(ctx[i].cmpFlags)); + fmgr_info(cmpFn, &(ctx[i].cmpFn)); + + qsort_arg((void *) stats->stavalues[i][0], stats->numvalues[i][0], sizeof(Datum), + compare_datums, (void *) &ctx[i]); + } + else + fmgr_info(mystats->eqfunc, &(ctx[i].cmpFn)); + } + + /* Free up previously computed stanumbers arrays. */ + for (i = 0; i < STATISTIC_NUM_SLOTS; i++) + for (j = 0; j < STATISTIC_NUM_SLOTS; j++) + { + if (stats->stanumbers[i][j]) + { + pfree(stats->stanumbers[i][j]); + stats->stanumbers[i][j] = NULL; + } + stats->numnumbers[i][j] = 0; + } + + /* + * Compute the size of the N-dimension histogram and the dimension stride values. + * The histogram cells at the 0th index of a dimension is the nullfrac value, + * hence the + 1. + */ + for (i = 0, numvalues = 1; i < stats->attnums->dim1; i++) + { + dimstride[stats->attnums->dim1 - i - 1] = numvalues; + numvalues *= stats->numvalues[i][0] + 1; + } + + old_context = MemoryContextSwitchTo(stats->anl_context); + + hist = (float *) palloc(numvalues * sizeof(float)); + for (i = 0; i < numvalues; i++) + hist[i] = 0.0; + + MemoryContextSwitchTo(old_context); + + for (i = 0; i < numrows; i++) + { + int idx, idx_tmp; + + idx = 0; + for (j = 0; j < stats->attnums->dim1; j++) + { + idx_tmp = stats->statfuncs[j].findval_func_ptr(stats, i, j, fetchfunc, &ctx[j]); + if (idx_tmp < 0) + continue; + + idx += idx_tmp * dimstride[j]; + } + hist[idx] = hist[idx] + 1.0; + } + + /* Store selectivity instead of actual counts. */ + for (i = 0; i < numvalues; i ++) + hist[i] /= numrows; + + stats->numnumbers[0][0] = numvalues; + stats->stanumbers[0][0] = hist; + } + + /* * qsort comparator for sorting ScalarMCVItems by position */ static int *************** compare_mcvs(const void *a, const void * *** 2739,2741 **** --- 3248,3321 ---- return da - db; } + + /* + * ExtraColStat + * Add or remove one extra entry in pg_statistics + */ + void ExtraStatistics(ExtraStatStmt *stmt) + { + Oid relId; + int len, i, j; + bool differ = false; + AttrNumber *attnums; + AttrNumber *sorted_attnums; + ListCell *l; + + relId = RangeVarGetRelid(stmt->relation, AccessExclusiveLock, false, false); + + len = list_length(stmt->columns); + if (len < 2) + elog(ERROR, "cross column statistics need at least two columns"); + if (len > STATISTIC_NUM_SLOTS) + elog(ERROR, "cross column statistics can cover at most %d columns", STATISTIC_NUM_SLOTS); + + attnums = (int2 *)palloc(len * sizeof(AttrNumber)); + sorted_attnums = (int2 *)palloc(len * sizeof(AttrNumber)); + + i = 0; + foreach(l, stmt->columns) + { + Node *node = (Node *) lfirst(l); + Var *var; + + if (!IsA(node, Var)) + elog(ERROR, "not a column reference"); + + var = (Var *) node; + + if (var->varattno == 0) + elog(ERROR, "row expansion via \"*\" is not supported here"); + + sorted_attnums[i] = attnums[i] = var->varattno; + + i++; + } + + for (i = 0; i < len - 1; i++) + for (j = i+1; j < len; j++) + if (sorted_attnums[i] > sorted_attnums[j]) + { + AttrNumber tmp = sorted_attnums[i]; + + sorted_attnums[i] = sorted_attnums[j]; + sorted_attnums[j] = tmp; + } + + for (i = 0; i < len; i++) + { + if (!differ && attnums[i] != sorted_attnums[i]) + differ = true; + + if ((i < len - 1) && sorted_attnums[i] == sorted_attnums[i+1]) + elog(ERROR, "column list must contain every column exactly once"); + + } + if (differ) + elog(WARNING, "the column list was reordered in the order of table attributes"); + + if (stmt->create) + AddStatistics(relId, sorted_attnums, len, false, stmt->statistics_target); + else + RemoveStatistics(relId, sorted_attnums, len); + } diff -dcrpN postgresql.orig/src/backend/commands/tablecmds.c postgresql/src/backend/commands/tablecmds.c *** postgresql.orig/src/backend/commands/tablecmds.c 2011-09-05 15:08:06.505567943 +0200 --- postgresql/src/backend/commands/tablecmds.c 2011-09-12 10:01:16.543508362 +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 *** 4341,4347 **** 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; --- 4342,4347 ---- *************** ATExecAddColumn(List **wqueue, AlteredTa *** 4503,4508 **** --- 4503,4510 ---- 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 *** 4839,4846 **** --- 4841,4854 ---- { 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 *** 4866,4872 **** attrelation = heap_open(AttributeRelationId, RowExclusiveLock); ! tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); if (!HeapTupleIsValid(tuple)) ereport(ERROR, --- 4874,4882 ---- attrelation = heap_open(AttributeRelationId, RowExclusiveLock); ! relid = RelationGetRelid(rel); ! ! tuple = SearchSysCacheAttName(relid, colName); if (!HeapTupleIsValid(tuple)) ereport(ERROR, *************** ATExecSetStatistics(Relation rel, const *** 4881,4896 **** 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 --- 4891,4931 ---- 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 * *** 7390,7398 **** 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 --- 7425,7433 ---- 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/src/backend/executor/nodeHash.c *** postgresql.orig/src/backend/executor/nodeHash.c 2011-09-02 12:52:41.347575337 +0200 --- postgresql/src/backend/executor/nodeHash.c 2011-09-12 10:01:16.578506729 +0200 *************** *** 32,37 **** --- 32,38 ---- #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" #include "miscadmin.h" + #include "utils/builtins.h" #include "utils/dynahash.h" #include "utils/memutils.h" #include "utils/lsyscache.h" *************** ExecHashBuildSkewHash(HashJoinTable hash *** 1125,1130 **** --- 1126,1132 ---- 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 *** 1136,1145 **** /* * 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; --- 1138,1160 ---- /* * 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/nodes/copyfuncs.c postgresql/src/backend/nodes/copyfuncs.c *** postgresql.orig/src/backend/nodes/copyfuncs.c 2011-09-05 15:08:06.513567349 +0200 --- postgresql/src/backend/nodes/copyfuncs.c 2011-09-12 10:01:16.585506400 +0200 *************** _copyCreateForeignTableStmt(CreateForeig *** 3455,3460 **** --- 3455,3473 ---- return newnode; } + static ExtraStatStmt * + _copyExtraStatStmt(ExtraStatStmt *from) + { + ExtraStatStmt *newnode = makeNode(ExtraStatStmt); + + COPY_SCALAR_FIELD(create); + newnode->relation = _copyRangeVar(from->relation); + COPY_NODE_FIELD(columns); + COPY_SCALAR_FIELD(statistics_target); + + return newnode; + } + static CreateTrigStmt * _copyCreateTrigStmt(CreateTrigStmt *from) { *************** copyObject(void *from) *** 4374,4379 **** --- 4387,4395 ---- case T_CreateForeignTableStmt: retval = _copyCreateForeignTableStmt(from); break; + case T_ExtraStatStmt: + retval = _copyExtraStatStmt(from); + break; case T_CreateTrigStmt: retval = _copyCreateTrigStmt(from); break; diff -dcrpN postgresql.orig/src/backend/nodes/equalfuncs.c postgresql/src/backend/nodes/equalfuncs.c *** postgresql.orig/src/backend/nodes/equalfuncs.c 2011-07-24 18:16:45.269678833 +0200 --- postgresql/src/backend/nodes/equalfuncs.c 2011-09-12 10:01:16.605505468 +0200 *************** _equalCreateForeignTableStmt(CreateForei *** 1796,1801 **** --- 1796,1813 ---- } static bool + _equalExtraStatStmt(ExtraStatStmt *a, ExtraStatStmt *b) + { + COMPARE_SCALAR_FIELD(create); + if (!_equalRangeVar(a->relation, b->relation)) + return FALSE; + COMPARE_NODE_FIELD(columns); + COMPARE_SCALAR_FIELD(statistics_target); + + return true; + } + + static bool _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b) { COMPARE_STRING_FIELD(trigname); *************** equal(void *a, void *b) *** 2931,2936 **** --- 2943,2951 ---- case T_CreateForeignTableStmt: retval = _equalCreateForeignTableStmt(a, b); break; + case T_ExtraStatStmt: + retval = _equalExtraStatStmt(a, b); + break; case T_CreateTrigStmt: retval = _equalCreateTrigStmt(a, b); break; diff -dcrpN postgresql.orig/src/backend/parser/gram.y postgresql/src/backend/parser/gram.y *** postgresql.orig/src/backend/parser/gram.y 2011-08-07 11:29:16.020256051 +0200 --- postgresql/src/backend/parser/gram.y 2011-09-12 10:01:16.618504860 +0200 *************** static void processCASbits(int cas_bits, *** 214,220 **** DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt ! DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt --- 214,220 ---- DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt ! DropForeignServerStmt DropUserMappingStmt ExplainStmt ExtraStatStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt *************** static void processCASbits(int cas_bits, *** 246,252 **** transaction_mode_item create_extension_opt_item alter_extension_opt_item ! %type opt_lock lock_type cast_context %type vacuum_option_list vacuum_option_elem %type opt_force opt_or_replace opt_grant_grant_option opt_grant_admin_option --- 246,252 ---- transaction_mode_item create_extension_opt_item alter_extension_opt_item ! %type opt_lock lock_type cast_context opt_stattarget %type vacuum_option_list vacuum_option_elem %type opt_force opt_or_replace opt_grant_grant_option opt_grant_admin_option *************** static void processCASbits(int cas_bits, *** 325,330 **** --- 325,332 ---- %type opt_fdw_options fdw_options %type fdw_option + %type cc_column_list + %type OptTempTableName %type into_clause create_as_target *************** stmt : *** 756,761 **** --- 758,764 ---- | DropdbStmt | ExecuteStmt | ExplainStmt + | ExtraStatStmt | FetchStmt | GrantStmt | GrantRoleStmt *************** schema_stmt: *** 1200,1205 **** --- 1203,1276 ---- /***************************************************************************** * + * Add / drop extra statistics + * + *****************************************************************************/ + + ExtraStatStmt: + CREATE CROSS COLUMN STATISTICS ON TABLE qualified_name '(' cc_column_list ')' opt_stattarget + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'r'; + n->create = true; + n->relation = $7; + n->columns = $9; + n->statistics_target = $11; + $$ = (Node *)n; + } + | DROP CROSS COLUMN STATISTICS ON TABLE qualified_name '(' cc_column_list ')' + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'r'; + n->create = false; + n->relation = $7; + n->columns = $9; + $$ = (Node *)n; + } + | CREATE CROSS COLUMN STATISTICS ON INDEX qualified_name opt_stattarget + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'i'; + n->create = true; + n->relation = $7; + n->columns = NIL; + n->statistics_target = $8; + $$ = (Node *)n; + } + | DROP CROSS COLUMN STATISTICS ON INDEX qualified_name + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'i'; + n->create = false; + n->relation = $7; + n->columns = NIL; + $$ = (Node *)n; + } + ; + + cc_column_list: + columnref + { + $$ = list_make1($1); + } + | cc_column_list ',' columnref + { + $$ = lappend($1, $3); + } + ; + + opt_stattarget: + WITH '(' Iconst ')' { $$ = $3; } + | /* EMPTY */ { $$ = -1; } + ; + + + /***************************************************************************** + * * Set PG internal variable * SET name TO 'var_value' * Include SQL92 syntax (thomas 1997-10-22): diff -dcrpN postgresql.orig/src/backend/parser/parse_utilcmd.c postgresql/src/backend/parser/parse_utilcmd.c *** postgresql.orig/src/backend/parser/parse_utilcmd.c 2011-09-02 12:52:41.361574202 +0200 --- postgresql/src/backend/parser/parse_utilcmd.c 2011-09-12 10:01:16.630504299 +0200 *************** setSchemaName(char *context_schema, char *** 2732,2734 **** --- 2732,2827 ---- "different from the one being created (%s)", *stmt_schema_name, context_schema))); } + + /* + * transformExtraStatistics + * Transform the column list or the expression into a form + * usable by the executor. + */ + ExtraStatStmt * + transformExtraStatistics(ExtraStatStmt *stmt, const char *queryString) + { + ParseState *pstate; + RangeTblEntry *rte; + ExtraStatStmt *newstmt; + List *columns = NIL; + ListCell *cell; + Oid relId; + HeapTuple tuple; + HeapTuple attuple; + Form_pg_class classptr; + Form_pg_index indexptr; + Form_pg_attribute attptr; + AttrNumber i; + + switch (stmt->relkind) + { + case 'r': + pstate = make_parsestate(NULL); + pstate->p_sourcetext = queryString; + + rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); + addRTEtoQuery(pstate, rte, true, true, true); + + foreach(cell, stmt->columns) + { + Node *col = lfirst(cell); + + columns = lappend(columns, transformExpr(pstate, col)); + } + + break; + + case 'i': + relId = RangeVarGetRelid(stmt->relation, ShareLock, false, false); + + tuple = SearchSysCache1(RELOID, relId); + classptr = (Form_pg_class) GETSTRUCT(tuple); + + if (classptr->relkind != 'i') + elog(ERROR, "not an index"); + + ReleaseSysCache(tuple); + + tuple = SearchSysCache1(INDEXRELID, relId); + indexptr = (Form_pg_index) GETSTRUCT(tuple); + + if (indexptr->indnatts < 2) + { + ReleaseSysCache(tuple); + + elog(ERROR, "cross column statistics are only usable on multi-column indexes"); + } + + for (i = 1; i <= indexptr->indnatts; i++) + { + attuple = SearchSysCache2(ATTNUM, relId, i); + if (!HeapTupleIsValid(attuple)) + elog(ERROR, "pg_attribute row not found for index"); + + attptr = (Form_pg_attribute) GETSTRUCT(attuple); + + columns = lappend(columns, makeVar(0, i, + attptr->atttypid, + attptr->atttypmod, + InvalidOid, 0)); + + ReleaseSysCache(attuple); + } + + ReleaseSysCache(tuple); + break; + + default: + elog(ERROR, "invalid relkind"); + } + + newstmt = makeNode(ExtraStatStmt); + newstmt->relkind = stmt->relkind; + newstmt->create = stmt->create; + newstmt->relation = copyObject(stmt->relation); + newstmt->columns = columns; + newstmt->statistics_target = stmt->statistics_target; + + return newstmt; + } diff -dcrpN postgresql.orig/src/backend/tcop/utility.c postgresql/src/backend/tcop/utility.c *** postgresql.orig/src/backend/tcop/utility.c 2011-07-24 18:16:45.276678481 +0200 --- postgresql/src/backend/tcop/utility.c 2011-09-12 10:01:16.639503878 +0200 *************** check_xact_readonly(Node *parsetree) *** 237,242 **** --- 237,243 ---- case T_AlterTableSpaceOptionsStmt: case T_CreateForeignTableStmt: case T_SecLabelStmt: + case T_ExtraStatStmt: PreventCommandIfReadOnly(CreateCommandTag(parsetree)); break; default: *************** standard_ProcessUtility(Node *parsetree, *** 581,586 **** --- 582,595 ---- } break; + case T_ExtraStatStmt: + { + ExtraStatStmt *newstmt = transformExtraStatistics((ExtraStatStmt *)parsetree, queryString); + + ExtraStatistics(newstmt); + } + break; + case T_CreateTableSpaceStmt: PreventTransactionChain(isTopLevel, "CREATE TABLESPACE"); CreateTableSpace((CreateTableSpaceStmt *) parsetree); *************** CreateCommandTag(Node *parsetree) *** 1744,1749 **** --- 1753,1769 ---- tag = "CREATE FOREIGN TABLE"; break; + case T_ExtraStatStmt: + { + ExtraStatStmt *stmt = (ExtraStatStmt *)parsetree; + + if (stmt->create) + tag = "CREATE CROSS COLUMN STATISTICS"; + else + tag = "DROP CROSS COLUMN STATISTICS"; + } + break; + case T_DropStmt: switch (((DropStmt *) parsetree)->removeType) { diff -dcrpN postgresql.orig/src/backend/tsearch/ts_typanalyze.c postgresql/src/backend/tsearch/ts_typanalyze.c *** postgresql.orig/src/backend/tsearch/ts_typanalyze.c 2011-09-02 12:52:41.368573634 +0200 --- postgresql/src/backend/tsearch/ts_typanalyze.c 2011-09-12 10:01:16.650503364 +0200 *************** typedef struct *** 35,41 **** int delta; /* And this is 'delta'. */ } TrackItem; ! static void compute_tsvector_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows); --- 35,42 ---- int delta; /* And this is 'delta'. */ } TrackItem; ! static void compute_tsvector_stats(VacStats *stats, ! int index, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows); *************** static int trackitem_compare_lexemes(con *** 53,69 **** Datum 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); } --- 54,74 ---- Datum ts_typanalyze(PG_FUNCTION_ARGS) { ! VacStats *stats = (VacStats *) PG_GETARG_POINTER(0); ! int4 i = PG_GETARG_INT32(1); ! if (i < 0 || i >= STATISTIC_NUM_SLOTS) ! PG_RETURN_BOOL(false); ! /* 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->statfuncs[i].compute_func_ptr = compute_tsvector_stats; ! stats->statfuncs[i].findval_func_ptr = NULL; /* this prevents cross-column stats to be computed */ /* see comment about the choice of minrows in commands/analyze.c */ ! stats->minrows = 300 * stats->statarget; PG_RETURN_BOOL(true); } *************** ts_typanalyze(PG_FUNCTION_ARGS) *** 136,142 **** * want. */ static void ! compute_tsvector_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) --- 141,148 ---- * want. */ static void ! compute_tsvector_stats(VacStats *stats, ! int index, AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows) *************** compute_tsvector_stats(VacAttrStats *sta *** 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->attr->attstattarget * 10; /* * We set bucket width equal to (num_mcelem + 10) / 0.007 as per the --- 172,178 ---- * 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 *************** compute_tsvector_stats(VacAttrStats *sta *** 206,212 **** vacuum_delay_point(); ! value = fetchfunc(stats, vector_no, &isnull); /* * Check for null/nonnull. --- 212,218 ---- vacuum_delay_point(); ! value = fetchfunc(stats, vector_no, stats->attnums->values[index], &isnull); /* * Check for null/nonnull. *************** compute_tsvector_stats(VacAttrStats *sta *** 291,301 **** stats->stats_valid = true; /* Do the simple null-frac and average width stats */ ! stats->stanullfrac = (double) null_cnt / (double) samplerows; ! stats->stawidth = total_width / (double) nonnull_cnt; /* Assume it's a unique column (see notes above) */ ! stats->stadistinct = -1.0; /* * Construct an array of the interesting hashtable items, that is, --- 297,307 ---- stats->stats_valid = true; /* Do the simple null-frac and average width stats */ ! stats->stanullfrac[index] = (double) null_cnt / (double) samplerows; ! stats->stawidth[index] = total_width / (double) nonnull_cnt; /* Assume it's a unique column (see notes above) */ ! stats->stadistinct[index] = -1.0; /* * Construct an array of the interesting hashtable items, that is, *************** compute_tsvector_stats(VacAttrStats *sta *** 398,424 **** mcelem_freqs[i] = (double) maxfreq / (double) nonnull_cnt; MemoryContextSwitchTo(old_context); ! stats->stakind[0] = STATISTIC_KIND_MCELEM; ! stats->staop[0] = TextEqualOperator; ! stats->stanumbers[0] = mcelem_freqs; /* See above comment about two extra frequency fields */ ! stats->numnumbers[0] = num_mcelem + 2; ! stats->stavalues[0] = mcelem_values; ! stats->numvalues[0] = num_mcelem; /* We are storing text values */ ! stats->statypid[0] = TEXTOID; ! stats->statyplen[0] = -1; /* typlen, -1 for varlena */ ! stats->statypbyval[0] = false; ! stats->statypalign[0] = 'i'; } } else { /* We found only nulls; assume the column is entirely null */ stats->stats_valid = true; ! stats->stanullfrac = 1.0; ! stats->stawidth = 0; /* "unknown" */ ! stats->stadistinct = 0.0; /* "unknown" */ } /* --- 404,430 ---- mcelem_freqs[i] = (double) maxfreq / (double) nonnull_cnt; MemoryContextSwitchTo(old_context); ! stats->stakind[index][0] = STATISTIC_KIND_MCELEM; ! stats->staop[index][0] = TextEqualOperator; ! stats->stanumbers[index][0] = mcelem_freqs; /* See above comment about two extra frequency fields */ ! stats->numnumbers[index][0] = num_mcelem + 2; ! stats->stavalues[index][0] = mcelem_values; ! stats->numvalues[index][0] = num_mcelem; /* We are storing text values */ ! stats->statypid[index][0] = TEXTOID; ! stats->statyplen[index][0] = -1; /* typlen, -1 for varlena */ ! stats->statypbyval[index][0] = false; ! stats->statypalign[index][0] = 'i'; } } else { /* We found only nulls; assume the column is entirely null */ stats->stats_valid = true; ! stats->stanullfrac[index] = 1.0; ! stats->stawidth[index] = 0; /* "unknown" */ ! stats->stadistinct[index] = 0.0; /* "unknown" */ } /* diff -dcrpN postgresql.orig/src/backend/utils/adt/int.c postgresql/src/backend/utils/adt/int.c *** postgresql.orig/src/backend/utils/adt/int.c 2011-09-05 15:08:06.528566237 +0200 --- postgresql/src/backend/utils/adt/int.c 2011-09-12 10:01:16.665502664 +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/src/backend/utils/adt/selfuncs.c *** postgresql.orig/src/backend/utils/adt/selfuncs.c 2011-09-12 09:54:31.184313261 +0200 --- postgresql/src/backend/utils/adt/selfuncs.c 2011-09-12 10:01:16.679502009 +0200 *************** get_join_variables(PlannerInfo *root, Li *** 4105,4110 **** --- 4105,4138 ---- } /* + * 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 *** 4292,4303 **** } else if (index->indpred == NIL) { ! vardata->statsTuple = ! SearchSysCache3(STATRELATTINH, ! ObjectIdGetDatum(index->indexoid), ! Int16GetDatum(pos + 1), ! BoolGetDatum(false)); ! vardata->freefunc = ReleaseSysCache; } if (vardata->statsTuple) break; --- 4320,4328 ---- } else if (index->indpred == NIL) { ! int2 attnum = pos + 1; ! ! validate_statistics(vardata, index->indexoid, &attnum, 1, false); } if (vardata->statsTuple) break; *************** examine_simple_variable(PlannerInfo *roo *** 4345,4355 **** * Plain table or parent of an inheritance appendrel, so look up the * column in pg_statistic */ ! vardata->statsTuple = SearchSysCache3(STATRELATTINH, ! ObjectIdGetDatum(rte->relid), ! Int16GetDatum(var->varattno), ! BoolGetDatum(rte->inh)); ! vardata->freefunc = ReleaseSysCache; } else if (rte->rtekind == RTE_SUBQUERY && !rte->inh) { --- 4370,4376 ---- * Plain table or parent of an inheritance appendrel, so look up the * column in pg_statistic */ ! validate_statistics(vardata, rte->relid, &(var->varattno), 1, rte->inh); } else if (rte->rtekind == RTE_SUBQUERY && !rte->inh) { *************** btcostestimate(PG_FUNCTION_ARGS) *** 6406,6418 **** 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 { --- 6427,6433 ---- elog(ERROR, "no function provided to release variable stats with"); } else ! validate_statistics(&vardata, relid, &colnum, 1, rte->inh); } else { *************** btcostestimate(PG_FUNCTION_ARGS) *** 6432,6444 **** 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)) --- 6447,6453 ---- 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/src/backend/utils/cache/lsyscache.c *** postgresql.orig/src/backend/utils/cache/lsyscache.c 2011-07-18 15:42:00.066375563 +0200 --- postgresql/src/backend/utils/cache/lsyscache.c 2011-09-12 10:01:16.696501214 +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/src/backend/utils/cache/syscache.c *** postgresql.orig/src/backend/utils/cache/syscache.c 2011-06-20 10:11:35.741660316 +0200 --- postgresql/src/backend/utils/cache/syscache.c 2011-09-12 10:01:16.706500748 +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/src/include/catalog/heap.h *** postgresql.orig/src/include/catalog/heap.h 2011-07-24 18:16:45.286677978 +0200 --- postgresql/src/include/catalog/heap.h 2011-09-12 10:01:16.717500234 +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/src/include/catalog/indexing.h *** postgresql.orig/src/include/catalog/indexing.h 2011-07-24 18:16:45.286677978 +0200 --- postgresql/src/include/catalog/indexing.h 2011-09-12 10:01:16.748498784 +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/src/include/catalog/pg_amop.h *** postgresql.orig/src/include/catalog/pg_amop.h 2011-04-11 15:36:27.235807013 +0200 --- postgresql/src/include/catalog/pg_amop.h 2011-09-12 10:01:16.757498366 +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/src/include/catalog/pg_amproc.h *** postgresql.orig/src/include/catalog/pg_amproc.h 2011-01-04 15:13:16.120551585 +0100 --- postgresql/src/include/catalog/pg_amproc.h 2011-09-12 10:01:16.768497849 +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/src/include/catalog/pg_attribute.h *** postgresql.orig/src/include/catalog/pg_attribute.h 2011-08-07 11:29:16.032255410 +0200 --- postgresql/src/include/catalog/pg_attribute.h 2011-09-12 10:01:16.779497338 +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 *** 182,209 **** * ---------------- */ ! #define Natts_pg_attribute 21 #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 ! #define Anum_pg_attribute_attfdwoptions 21 /* ---------------- --- 173,199 ---- * ---------------- */ ! #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_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 ! #define Anum_pg_attribute_attfdwoptions 20 /* ---------------- diff -dcrpN postgresql.orig/src/include/catalog/pg_class.h postgresql/src/include/catalog/pg_class.h *** postgresql.orig/src/include/catalog/pg_class.h 2011-08-07 11:29:16.032255410 +0200 --- postgresql/src/include/catalog/pg_class.h 2011-09-12 10:01:16.790496821 +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 21 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 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(""); diff -dcrpN postgresql.orig/src/include/catalog/pg_opclass.h postgresql/src/include/catalog/pg_opclass.h *** postgresql.orig/src/include/catalog/pg_opclass.h 2011-01-04 15:13:16.125551330 +0100 --- postgresql/src/include/catalog/pg_opclass.h 2011-09-12 10:01:16.799496403 +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/src/include/catalog/pg_operator.h *** postgresql.orig/src/include/catalog/pg_operator.h 2011-06-06 09:12:54.421675553 +0200 --- postgresql/src/include/catalog/pg_operator.h 2011-09-12 10:01:16.814495702 +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/src/include/catalog/pg_opfamily.h *** postgresql.orig/src/include/catalog/pg_opfamily.h 2011-01-04 15:13:16.126551278 +0100 --- postgresql/src/include/catalog/pg_opfamily.h 2011-09-12 10:01:16.833494812 +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/src/include/catalog/pg_proc.h *** postgresql.orig/src/include/catalog/pg_proc.h 2011-07-18 15:42:00.078374691 +0200 --- postgresql/src/include/catalog/pg_proc.h 2011-09-12 10:01:16.849494065 +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_ )); *************** DATA(insert OID = 3686 ( tsmatchsel PG *** 4141,4147 **** DESCR("restriction selectivity of tsvector @@ tsquery"); DATA(insert OID = 3687 ( tsmatchjoinsel PGNSP PGUID 12 1 0 0 0 f f f t f s 5 0 701 "2281 26 2281 21 2281" _null_ _null_ _null_ _null_ tsmatchjoinsel _null_ _null_ _null_ )); DESCR("join selectivity of tsvector @@ tsquery"); ! DATA(insert OID = 3688 ( ts_typanalyze PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ ts_typanalyze _null_ _null_ _null_ )); DESCR("tsvector typanalyze"); DATA(insert OID = 3689 ( ts_stat PGNSP PGUID 12 10 10000 0 0 f f f t t v 1 0 2249 "25" "{25,25,23,23}" "{i,o,o,o}" "{query,word,ndoc,nentry}" _null_ ts_stat1 _null_ _null_ _null_ )); --- 4150,4156 ---- DESCR("restriction selectivity of tsvector @@ tsquery"); DATA(insert OID = 3687 ( tsmatchjoinsel PGNSP PGUID 12 1 0 0 0 f f f t f s 5 0 701 "2281 26 2281 21 2281" _null_ _null_ _null_ _null_ tsmatchjoinsel _null_ _null_ _null_ )); DESCR("join selectivity of tsvector @@ tsquery"); ! DATA(insert OID = 3688 ( ts_typanalyze PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ ts_typanalyze _null_ _null_ _null_ )); DESCR("tsvector typanalyze"); DATA(insert OID = 3689 ( ts_stat PGNSP PGUID 12 10 10000 0 0 f f f t t v 1 0 2249 "25" "{25,25,23,23}" "{i,o,o,o}" "{query,word,ndoc,nentry}" _null_ ts_stat1 _null_ _null_ _null_ )); diff -dcrpN postgresql.orig/src/include/catalog/pg_statistic.h postgresql/src/include/catalog/pg_statistic.h *** postgresql.orig/src/include/catalog/pg_statistic.h 2011-02-22 18:51:42.762512469 +0100 --- postgresql/src/include/catalog/pg_statistic.h 2011-09-12 10:01:16.877492756 +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/defrem.h postgresql/src/include/commands/defrem.h *** postgresql.orig/src/include/commands/defrem.h 2011-07-24 18:16:45.287677928 +0200 --- postgresql/src/include/commands/defrem.h 2011-09-12 10:01:16.899491728 +0200 *************** extern void RemoveAggregate(RemoveFuncSt *** 93,98 **** --- 93,101 ---- extern void RenameAggregate(List *name, List *args, const char *newname); extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId); + /* commands/analyze.c */ + extern void ExtraStatistics(ExtraStatStmt *stmt); + /* commands/opclasscmds.c */ extern void DefineOpClass(CreateOpClassStmt *stmt); extern void DefineOpFamily(CreateOpFamilyStmt *stmt); diff -dcrpN postgresql.orig/src/include/commands/vacuum.h postgresql/src/include/commands/vacuum.h *** postgresql.orig/src/include/commands/vacuum.h 2011-06-02 10:21:24.006634564 +0200 --- postgresql/src/include/commands/vacuum.h 2011-09-12 10:01:16.912491121 +0200 *************** *** 56,67 **** * This might change in some future release. *---------- */ ! typedef struct VacAttrStats *VacAttrStatsP; ! typedef Datum (*AnalyzeAttrFetchFunc) (VacAttrStatsP stats, int rownum, bool *isNull); ! typedef struct VacAttrStats { /* * These fields are set up by the main ANALYZE code before invoking the --- 56,84 ---- * This might change in some future release. *---------- */ ! typedef struct VacStats *VacStatsP; ! typedef Datum (*AnalyzeAttrFetchFunc) (VacStatsP stats, int rownum, AttrNumber tupattnum, bool *isNull); ! typedef void (*ComputeStatsFunc) (VacStatsP stats, ! int index, ! AnalyzeAttrFetchFunc fetchfunc, ! int samplerows, ! double totalrows); ! ! typedef int (*FindValueIndex) (VacStatsP stats, ! int rownum, ! int index, ! AnalyzeAttrFetchFunc fetchfunc, ! void *arg); ! ! typedef struct StatsFuncStruct { ! ComputeStatsFunc compute_func_ptr; ! FindValueIndex findval_func_ptr; ! } StatsFuncStruct; ! ! typedef struct VacStats { /* * These fields are set up by the main ANALYZE code before invoking the *************** typedef struct VacAttrStats *** 73,109 **** * column/expression. Instead use attrtypid, attrtypmod, and attrtype for * information about the datatype being fed to the typanalyze function. */ ! 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 */ MemoryContext anl_context; /* where to save long-lived data */ /* * These fields must be filled in by the typanalyze routine, unless it * returns FALSE. */ ! void (*compute_stats) (VacAttrStatsP stats, ! AnalyzeAttrFetchFunc fetchfunc, ! int samplerows, ! double totalrows); int minrows; /* Minimum # of rows wanted for stats */ ! void *extra_data; /* for extra type-specific data */ /* * These fields are to be filled in by the compute_stats routine. (They * are initialized to zero when the struct is created.) */ bool stats_valid; ! float4 stanullfrac; /* fraction of entries that are NULL */ ! int4 stawidth; /* average width of column values */ ! float4 stadistinct; /* # distinct values */ ! int2 stakind[STATISTIC_NUM_SLOTS]; ! Oid staop[STATISTIC_NUM_SLOTS]; ! int numnumbers[STATISTIC_NUM_SLOTS]; ! float4 *stanumbers[STATISTIC_NUM_SLOTS]; ! int numvalues[STATISTIC_NUM_SLOTS]; ! Datum *stavalues[STATISTIC_NUM_SLOTS]; /* * These fields describe the stavalues[n] element types. They will be --- 90,126 ---- * column/expression. Instead use attrtypid, attrtypmod, and attrtype for * information about the datatype being fed to the typanalyze function. */ ! Form_pg_attribute attrs[STATISTIC_NUM_SLOTS]; /* copy of pg_attribute row for column */ ! int2vector *attnums; /* array of attributes this statistics is for */ ! int4 statarget; /* effective statistics target */ ! Oid attrtypids[STATISTIC_NUM_SLOTS]; /* type of data being analyzed */ ! int32 attrtypmods[STATISTIC_NUM_SLOTS]; /* typmod of data being analyzed */ ! Form_pg_type attrtypes[STATISTIC_NUM_SLOTS]; /* copy of pg_type row for attrtypid */ MemoryContext anl_context; /* where to save long-lived data */ /* * These fields must be filled in by the typanalyze routine, unless it * returns FALSE. */ ! ! StatsFuncStruct statfuncs[STATISTIC_NUM_SLOTS]; int minrows; /* Minimum # of rows wanted for stats */ ! void *extra_data[STATISTIC_NUM_SLOTS]; /* for extra type-specific data */ /* * These fields are to be filled in by the compute_stats routine. (They * are initialized to zero when the struct is created.) */ bool stats_valid; ! float4 stanullfrac[STATISTIC_NUM_SLOTS]; /* fraction of entries that are NULL */ ! int4 stawidth[STATISTIC_NUM_SLOTS]; /* average width of column values */ ! float4 stadistinct[STATISTIC_NUM_SLOTS]; /* # distinct values */ ! int2 stakind[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! Oid staop[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! int numnumbers[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! float4 *stanumbers[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! int numvalues[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! Datum *stavalues[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; /* * These fields describe the stavalues[n] element types. They will be *************** typedef struct VacAttrStats *** 111,132 **** * want to store an array of something other than the analyzed column's * elements. It should then overwrite these fields. */ ! Oid statypid[STATISTIC_NUM_SLOTS]; ! int2 statyplen[STATISTIC_NUM_SLOTS]; ! bool statypbyval[STATISTIC_NUM_SLOTS]; ! char statypalign[STATISTIC_NUM_SLOTS]; /* * These fields are private to the main ANALYZE code and should not be * looked at by type-specific functions. */ - int tupattnum; /* attribute number within tuples */ HeapTuple *rows; /* access info for std fetch function */ TupleDesc tupDesc; Datum *exprvals; /* access info for index fetch function */ bool *exprnulls; int rowstride; ! } VacAttrStats; /* GUC parameters */ --- 128,148 ---- * want to store an array of something other than the analyzed column's * elements. It should then overwrite these fields. */ ! Oid statypid[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! int2 statyplen[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! bool statypbyval[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; ! char statypalign[STATISTIC_NUM_SLOTS][STATISTIC_NUM_SLOTS]; /* * These fields are private to the main ANALYZE code and should not be * looked at by type-specific functions. */ HeapTuple *rows; /* access info for std fetch function */ TupleDesc tupDesc; Datum *exprvals; /* access info for index fetch function */ bool *exprnulls; int rowstride; ! } VacStats; /* GUC parameters */ diff -dcrpN postgresql.orig/src/include/nodes/nodes.h postgresql/src/include/nodes/nodes.h *** postgresql.orig/src/include/nodes/nodes.h 2011-08-07 11:29:16.032255410 +0200 --- postgresql/src/include/nodes/nodes.h 2011-09-12 10:01:16.929490327 +0200 *************** typedef enum NodeTag *** 362,367 **** --- 362,368 ---- T_CreateExtensionStmt, T_AlterExtensionStmt, T_AlterExtensionContentsStmt, + T_ExtraStatStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff -dcrpN postgresql.orig/src/include/nodes/parsenodes.h postgresql/src/include/nodes/parsenodes.h *** postgresql.orig/src/include/nodes/parsenodes.h 2011-08-07 11:29:16.033255357 +0200 --- postgresql/src/include/nodes/parsenodes.h 2011-09-12 10:01:16.939489863 +0200 *************** typedef enum DropBehavior *** 1161,1166 **** --- 1161,1180 ---- } DropBehavior; /* ---------------------- + * Create Cross Column Statistics + * ---------------------- + */ + typedef struct ExtraStatStmt + { + NodeTag type; + char relkind; + bool create; + RangeVar *relation; + List *columns; + int statistics_target; + } ExtraStatStmt; + + /* ---------------------- * Alter Table * ---------------------- */ diff -dcrpN postgresql.orig/src/include/parser/parse_utilcmd.h postgresql/src/include/parser/parse_utilcmd.h *** postgresql.orig/src/include/parser/parse_utilcmd.h 2011-01-04 15:13:16.163549374 +0100 --- postgresql/src/include/parser/parse_utilcmd.h 2011-09-12 10:01:16.953489208 +0200 *************** extern void transformRuleStmt(RuleStmt * *** 25,28 **** --- 25,31 ---- List **actions, Node **whereClause); extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt); + extern ExtraStatStmt *transformExtraStatistics(ExtraStatStmt *stmt, + const char *queryString); + #endif /* PARSE_UTILCMD_H */ diff -dcrpN postgresql.orig/src/include/utils/builtins.h postgresql/src/include/utils/builtins.h *** postgresql.orig/src/include/utils/builtins.h 2011-06-24 11:38:23.342905815 +0200 --- postgresql/src/include/utils/builtins.h 2011-09-12 10:01:16.963488738 +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/src/include/utils/selfuncs.h *** postgresql.orig/src/include/utils/selfuncs.h 2011-09-05 15:08:06.539565424 +0200 --- postgresql/src/include/utils/selfuncs.h 2011-09-12 10:01:16.988487572 +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/src/test/regress/expected/rules.out *** postgresql.orig/src/test/regress/expected/rules.out 2011-07-24 18:16:45.313676620 +0200 --- postgresql/src/test/regress/expected/rules.out 2011-09-12 10:01:16.999487056 +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); diff -dcrpN postgresql.orig/src/test/regress/expected/type_sanity.out postgresql/src/test/regress/expected/type_sanity.out *** postgresql.orig/src/test/regress/expected/type_sanity.out 2011-01-04 15:13:16.217546597 +0100 --- postgresql/src/test/regress/expected/type_sanity.out 2011-09-12 10:01:17.017486217 +0200 *************** WHERE p1.typarray = p2.oid AND NOT (p1.t *** 267,274 **** SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT ! (p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype AND p2.prorettype = 'bool'::regtype AND NOT p2.proretset); oid | typname | oid | proname -----+---------+-----+--------- --- 267,275 ---- SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT ! (p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND + p2.proargtypes[1] = 'int4'::regtype AND p2.prorettype = 'bool'::regtype AND NOT p2.proretset); oid | typname | oid | proname -----+---------+-----+--------- diff -dcrpN postgresql.orig/src/test/regress/sql/type_sanity.sql postgresql/src/test/regress/sql/type_sanity.sql *** postgresql.orig/src/test/regress/sql/type_sanity.sql 2011-01-04 15:13:16.220546441 +0100 --- postgresql/src/test/regress/sql/type_sanity.sql 2011-09-12 10:01:17.029485654 +0200 *************** WHERE p1.typarray = p2.oid AND NOT (p1.t *** 207,214 **** SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT ! (p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype AND p2.prorettype = 'bool'::regtype AND NOT p2.proretset); -- **************** pg_class **************** --- 207,215 ---- SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT ! (p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND + p2.proargtypes[1] = 'int4'::regtype AND p2.prorettype = 'bool'::regtype AND NOT p2.proretset); -- **************** pg_class ****************