From 659f79be7a6114117bff9fcaba05df18679f2610 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Mon, 29 May 2000 15:44:55 +0000 Subject: [PATCH] Allow vacuum to perform analyze with shared lock. Update cvs manual. --- doc/src/sgml/cvs.sgml | 6 +- src/backend/commands/vacuum.c | 815 ++++++++++++++++++---------------- src/include/commands/vacuum.h | 4 +- 3 files changed, 444 insertions(+), 381 deletions(-) diff --git a/doc/src/sgml/cvs.sgml b/doc/src/sgml/cvs.sgml index 50f9a0ccbc..bf2ee60ebc 100644 --- a/doc/src/sgml/cvs.sgml +++ b/doc/src/sgml/cvs.sgml @@ -1,5 +1,5 @@ @@ -184,7 +184,7 @@ cvs commit Do an initial login to the CVS server: -$ cvs -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot login +$ cvs -d :pserver:anoncvs@postgresql.org:/home/projects/pgsql/cvsroot login You will be prompted for a password; enter 'postgresql'. @@ -197,7 +197,7 @@ $ cvs -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot login Fetch the Postgres sources: -cvs -z3 -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot co -P pgsql +cvs -z3 -d :pserver:anoncvs@postgresql.org:/home/projects/pgsql/cvsroot co -P pgsql which installs the Postgres sources into a diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 3160e20110..bd8386be37 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.151 2000/05/29 01:55:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.152 2000/05/29 15:44:55 momjian Exp $ * *------------------------------------------------------------------------- @@ -77,15 +77,17 @@ static void vacuum_shutdown(void); static void vac_vacuum(NameData *VacRelP, bool analyze, List *va_cols); static VRelList getrels(NameData *VacRelP); static void vacuum_rel(Oid relid, bool analyze, List *va_cols); +static void analyze_rel(Oid relid, List *va_cols); static void scan_heap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages); static void repair_frag(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages, int nindices, Relation *Irel); static void vacuum_heap(VRelStats *vacrelstats, Relation onerel, VPageList vpl); static void vacuum_page(Page page, VPageDescr vpd); static void vacuum_index(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples); static void scan_index(Relation indrel, int num_tuples); -static void attr_stats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple); +static void attr_stats(Relation onerel, int attr_cnt, VacAttrStats *vacattrstats, HeapTuple tuple); static void bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len); -static void update_stats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats); +static void update_relstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats); +static void update_attstats(Oid relid, int natts, VacAttrStats *vacattrstats); static void del_stats(Oid relid, int attcnt, int *attnums); static VPageDescr tid_reaped(ItemPointer itemptr, VPageList vpl); static void reap_page(VPageList vpl, VPageDescr vpc); @@ -100,63 +102,7 @@ static int vac_cmp_offno(const void *left, const void *right); static int vac_cmp_vtlinks(const void *left, const void *right); static bool enough_space(VPageDescr vpd, Size len); static char *show_rusage(struct rusage * ru0); - - -/* - * This routines handle a special cross-transaction portal. - * However it is automatically closed in case of abort. - */ -void -CommonSpecialPortalOpen(void) -{ - char *pname; - - - if (CommonSpecialPortalInUse) - elog(ERROR, "CommonSpecialPortal is in use"); - - /* - * Create a portal for safe memory across transactions. We need to - * palloc the name space for it because our hash function expects the - * name to be on a longword boundary. CreatePortal copies the name to - * safe storage for us. - */ - pname = pstrdup(VACPNAME); - vac_portal = CreatePortal(pname); - pfree(pname); - - /* - * Set flag to indicate that vac_portal must be removed after an error. - * This global variable is checked in the transaction manager on xact - * abort, and the routine CommonSpecialPortalClose() is called if - * necessary. - */ - CommonSpecialPortalInUse = true; -} - -void -CommonSpecialPortalClose(void) -{ - /* Clear flag first, to avoid recursion if PortalDrop elog's */ - CommonSpecialPortalInUse = false; - - /* - * Release our portal for cross-transaction memory. - */ - PortalDrop(&vac_portal); -} - -PortalVariableMemory -CommonSpecialPortalGetMemory(void) -{ - return PortalGetVariableMemory(vac_portal); -} - -bool -CommonSpecialPortalIsOpen(void) -{ - return CommonSpecialPortalInUse; -} +/* CommonSpecialPortal function at the bottom */ void vacuum(char *vacrel, bool verbose, bool analyze, List *va_spec) @@ -299,6 +245,11 @@ vac_vacuum(NameData *VacRelP, bool analyze, List *va_cols) /* vacuum each heap relation */ for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) vacuum_rel(cur->vrl_relid, analyze, va_cols); + + /* analyze separately so locking is minimized */ + if (analyze) + for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) + analyze_rel(cur->vrl_relid, va_cols); } static VRelList @@ -410,8 +361,7 @@ getrels(NameData *VacRelP) static void vacuum_rel(Oid relid, bool analyze, List *va_cols) { - HeapTuple tuple, - typetuple; + HeapTuple tuple; Relation onerel; VPageListData vacuum_pages; /* List of pages to vacuum and/or clean * indices */ @@ -474,125 +424,6 @@ vacuum_rel(Oid relid, bool analyze, List *va_cols) vacrelstats->num_pages = vacrelstats->num_tuples = 0; vacrelstats->hasindex = false; - /* - * we can VACUUM ANALYZE any table except pg_statistic; see - * update_stats - */ - if (analyze && - strcmp(RelationGetRelationName(onerel), StatisticRelationName) != 0) - { - int attr_cnt, - *attnums = NULL; - Form_pg_attribute *attr; - - attr_cnt = onerel->rd_att->natts; - attr = onerel->rd_att->attrs; - - if (va_cols != NIL) - { - int tcnt = 0; - List *le; - - if (length(va_cols) > attr_cnt) - elog(ERROR, "vacuum: too many attributes specified for relation %s", - RelationGetRelationName(onerel)); - attnums = (int *) palloc(attr_cnt * sizeof(int)); - foreach(le, va_cols) - { - char *col = (char *) lfirst(le); - - for (i = 0; i < attr_cnt; i++) - { - if (namestrcmp(&(attr[i]->attname), col) == 0) - break; - } - if (i < attr_cnt) /* found */ - attnums[tcnt++] = i; - else - { - elog(ERROR, "vacuum: there is no attribute %s in %s", - col, RelationGetRelationName(onerel)); - } - } - attr_cnt = tcnt; - } - - vacrelstats->vacattrstats = (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats)); - - for (i = 0; i < attr_cnt; i++) - { - Operator func_operator; - Form_pg_operator pgopform; - VacAttrStats *stats; - - stats = &vacrelstats->vacattrstats[i]; - stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); - stats->best = stats->guess1 = stats->guess2 = 0; - stats->max = stats->min = 0; - stats->best_len = stats->guess1_len = stats->guess2_len = 0; - stats->max_len = stats->min_len = 0; - stats->initialized = false; - stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; - stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; - - func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true); - if (func_operator != NULL) - { - pgopform = (Form_pg_operator) GETSTRUCT(func_operator); - fmgr_info(pgopform->oprcode, &(stats->f_cmpeq)); - } - else - stats->f_cmpeq.fn_addr = NULL; - - func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true); - if (func_operator != NULL) - { - pgopform = (Form_pg_operator) GETSTRUCT(func_operator); - fmgr_info(pgopform->oprcode, &(stats->f_cmplt)); - stats->op_cmplt = oprid(func_operator); - } - else - { - stats->f_cmplt.fn_addr = NULL; - stats->op_cmplt = InvalidOid; - } - - func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); - if (func_operator != NULL) - { - pgopform = (Form_pg_operator) GETSTRUCT(func_operator); - fmgr_info(pgopform->oprcode, &(stats->f_cmpgt)); - } - else - stats->f_cmpgt.fn_addr = NULL; - - typetuple = SearchSysCacheTuple(TYPEOID, - ObjectIdGetDatum(stats->attr->atttypid), - 0, 0, 0); - if (HeapTupleIsValid(typetuple)) - { - stats->outfunc = ((Form_pg_type) GETSTRUCT(typetuple))->typoutput; - stats->typelem = ((Form_pg_type) GETSTRUCT(typetuple))->typelem; - } - else - { - stats->outfunc = InvalidOid; - stats->typelem = InvalidOid; - } - } - vacrelstats->va_natts = attr_cnt; - /* delete existing pg_statistic rows for relation */ - del_stats(relid, ((attnums) ? attr_cnt : 0), attnums); - if (attnums) - pfree(attnums); - } - else - { - vacrelstats->va_natts = 0; - vacrelstats->vacattrstats = (VacAttrStats *) NULL; - } - GetXmaxRecent(&XmaxRecent); /* scan it */ @@ -631,7 +462,7 @@ vacuum_rel(Oid relid, bool analyze, List *va_cols) vacuum_index(&vacuum_pages, Irel[i], vacrelstats->num_tuples, 0); } else -/* just scan indices to update statistic */ + /* just scan indices to update statistic */ { for (i = 0; i < nindices; i++) scan_index(Irel[i], vacrelstats->num_tuples); @@ -662,17 +493,197 @@ vacuum_rel(Oid relid, bool analyze, List *va_cols) pfree(fraged_pages.vpl_pagedesc); } - /* update statistics in pg_class */ - update_stats(vacrelstats->relid, vacrelstats->num_pages, - vacrelstats->num_tuples, vacrelstats->hasindex, vacrelstats); - /* all done with this class, but hold lock until commit */ heap_close(onerel, NoLock); + /* update statistics in pg_class */ + update_relstats(vacrelstats->relid, vacrelstats->num_pages, + vacrelstats->num_tuples, vacrelstats->hasindex, vacrelstats); + /* next command frees attribute stats */ CommitTransactionCommand(); } +/* + * analyze_rel() -- analyze relation + */ +static void +analyze_rel(Oid relid, List *va_cols) +{ + HeapTuple tuple, + typetuple; + Relation onerel; + int32 i; + int attr_cnt, + *attnums = NULL; + Form_pg_attribute *attr; + VacAttrStats *vacattrstats; + HeapScanDesc scan; + + StartTransactionCommand(); + + /* + * Check for user-requested abort. Note we want this to be inside a + * transaction, so xact.c doesn't issue useless NOTICE. + */ + if (QueryCancel) + CancelQuery(); + + /* + * Race condition -- if the pg_class tuple has gone away since the + * last time we saw it, we don't need to vacuum it. + */ + tuple = SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + /* + * We can VACUUM ANALYZE any table except pg_statistic. + * see update_relstats + */ + if (!HeapTupleIsValid(tuple) || + strcmp(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname), + StatisticRelationName) == 0) + { + CommitTransactionCommand(); + return; + } + + /* + * Open the class, get an exclusive lock on it, and check permissions. + * + * Note we choose to treat permissions failure as a NOTICE and keep + * trying to vacuum the rest of the DB --- is this appropriate? + */ + onerel = heap_open(relid, AccessShareLock); + +#ifndef NO_SECURITY + if (!pg_ownercheck(GetPgUserName(), RelationGetRelationName(onerel), + RELNAME)) + { + /* we already did an elog during vacuum + elog(NOTICE, "Skipping \"%s\" --- only table owner can VACUUM it", + RelationGetRelationName(onerel)); + */ + heap_close(onerel, AccessExclusiveLock); + CommitTransactionCommand(); + return; + } +#endif + + attr_cnt = onerel->rd_att->natts; + attr = onerel->rd_att->attrs; + + if (va_cols != NIL) + { + int tcnt = 0; + List *le; + + if (length(va_cols) > attr_cnt) + elog(ERROR, "vacuum: too many attributes specified for relation %s", + RelationGetRelationName(onerel)); + attnums = (int *) palloc(attr_cnt * sizeof(int)); + foreach(le, va_cols) + { + char *col = (char *) lfirst(le); + + for (i = 0; i < attr_cnt; i++) + { + if (namestrcmp(&(attr[i]->attname), col) == 0) + break; + } + if (i < attr_cnt) /* found */ + attnums[tcnt++] = i; + else + { + elog(ERROR, "vacuum: there is no attribute %s in %s", + col, RelationGetRelationName(onerel)); + } + } + attr_cnt = tcnt; + } + + vacattrstats = (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats)); + + for (i = 0; i < attr_cnt; i++) + { + Operator func_operator; + Form_pg_operator pgopform; + VacAttrStats *stats; + + stats = &vacattrstats[i]; + stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); + stats->best = stats->guess1 = stats->guess2 = 0; + stats->max = stats->min = 0; + stats->best_len = stats->guess1_len = stats->guess2_len = 0; + stats->max_len = stats->min_len = 0; + stats->initialized = false; + stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; + stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; + + func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + pgopform = (Form_pg_operator) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmpeq)); + } + else + stats->f_cmpeq.fn_addr = NULL; + + func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + pgopform = (Form_pg_operator) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmplt)); + stats->op_cmplt = oprid(func_operator); + } + else + { + stats->f_cmplt.fn_addr = NULL; + stats->op_cmplt = InvalidOid; + } + + func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + pgopform = (Form_pg_operator) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmpgt)); + } + else + stats->f_cmpgt.fn_addr = NULL; + + typetuple = SearchSysCacheTuple(TYPEOID, + ObjectIdGetDatum(stats->attr->atttypid), + 0, 0, 0); + if (HeapTupleIsValid(typetuple)) + { + stats->outfunc = ((Form_pg_type) GETSTRUCT(typetuple))->typoutput; + stats->typelem = ((Form_pg_type) GETSTRUCT(typetuple))->typelem; + } + else + { + stats->outfunc = InvalidOid; + stats->typelem = InvalidOid; + } + } + /* delete existing pg_statistic rows for relation */ + del_stats(relid, ((attnums) ? attr_cnt : 0), attnums); + + scan = heap_beginscan(onerel, false, SnapshotNow, 0, NULL); + + while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + attr_stats(onerel, attr_cnt, vacattrstats, tuple); + + heap_endscan(scan); + + heap_close(onerel, AccessShareLock); + + /* update statistics in pg_class */ + update_attstats(relid, attr_cnt, vacattrstats); + + CommitTransactionCommand(); +} + /* * scan_heap() -- scan an open heap relation * @@ -979,7 +990,6 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, min_tlen = tuple.t_len; if (tuple.t_len > max_tlen) max_tlen = tuple.t_len; - attr_stats(onerel, vacrelstats, &tuple); } } @@ -2106,7 +2116,7 @@ scan_index(Relation indrel, int num_tuples) /* now update statistics in pg_class */ nipages = RelationGetNumberOfBlocks(indrel); - update_stats(RelationGetRelid(indrel), nipages, nitups, false, NULL); + update_relstats(RelationGetRelid(indrel), nipages, nitups, false, NULL); elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %u. %s", RelationGetRelationName(indrel), nipages, nitups, @@ -2183,7 +2193,7 @@ vacuum_index(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples) /* now update statistics in pg_class */ num_pages = RelationGetNumberOfBlocks(indrel); - update_stats(RelationGetRelid(indrel), num_pages, num_index_tuples, false, NULL); + update_relstats(RelationGetRelid(indrel), num_pages, num_index_tuples, false, NULL); elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %u: Deleted %u. %s", RelationGetRelationName(indrel), num_pages, @@ -2263,11 +2273,9 @@ tid_reaped(ItemPointer itemptr, VPageList vpl) * */ static void -attr_stats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple) +attr_stats(Relation onerel, int attr_cnt, VacAttrStats *vacattrstats, HeapTuple tuple) { - int i, - attr_cnt = vacrelstats->va_natts; - VacAttrStats *vacattrstats = vacrelstats->vacattrstats; + int i; TupleDesc tupDesc = onerel->rd_att; Datum value; bool isnull; @@ -2387,7 +2395,7 @@ bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len) } /* - * update_stats() -- update statistics for one relation + * update_relstats() -- update statistics for one relation * * Statistics are stored in several places: the pg_class row for the * relation has stats about the whole relation, the pg_attribute rows @@ -2408,29 +2416,15 @@ bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len) * Updating pg_class's own statistics would be especially tricky. * Of course, this only works for fixed-size never-null columns, but * these are. - * - * Updates of pg_attribute statistics are handled in the same way - * for the same reasons. - * - * To keep things simple, we punt for pg_statistic, and don't try - * to compute or store rows for pg_statistic itself in pg_statistic. - * This could possibly be made to work, but it's not worth the trouble. */ static void -update_stats(Oid relid, int num_pages, int num_tuples, bool hasindex, +update_relstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats) { - Relation rd, - ad, - sd; - HeapScanDesc scan; + Relation rd; HeapTupleData rtup; - HeapTuple ctup, - atup, - stup; + HeapTuple ctup; Form_pg_class pgcform; - ScanKeyData askey; - Form_pg_attribute attp; Buffer buffer; /* @@ -2461,202 +2455,217 @@ update_stats(Oid relid, int num_pages, int num_tuples, bool hasindex, WriteBuffer(buffer); heap_close(rd, RowExclusiveLock); +} - if (vacrelstats != NULL && vacrelstats->va_natts > 0) - { - VacAttrStats *vacattrstats = vacrelstats->vacattrstats; - int natts = vacrelstats->va_natts; +/* + * update_attstats() -- update attribute statistics for one relation + * + * Updates of pg_attribute statistics are handled by over-write. + * for reasons described above. + * + * To keep things simple, we punt for pg_statistic, and don't try + * to compute or store rows for pg_statistic itself in pg_statistic. + * This could possibly be made to work, but it's not worth the trouble. + */ +static void +update_attstats(Oid relid, int natts, VacAttrStats *vacattrstats) +{ + Relation ad, + sd; + HeapScanDesc scan; + HeapTuple atup, + stup; + ScanKeyData askey; + Form_pg_attribute attp; - ad = heap_openr(AttributeRelationName, RowExclusiveLock); - sd = heap_openr(StatisticRelationName, RowExclusiveLock); + ad = heap_openr(AttributeRelationName, RowExclusiveLock); + sd = heap_openr(StatisticRelationName, RowExclusiveLock); - /* Find pg_attribute rows for this relation */ - ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, - F_INT4EQ, relid); + /* Find pg_attribute rows for this relation */ + ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, + F_INT4EQ, relid); - scan = heap_beginscan(ad, false, SnapshotNow, 1, &askey); + scan = heap_beginscan(ad, false, SnapshotNow, 1, &askey); - while (HeapTupleIsValid(atup = heap_getnext(scan, 0))) + while (HeapTupleIsValid(atup = heap_getnext(scan, 0))) + { + int i; + VacAttrStats *stats; + + attp = (Form_pg_attribute) GETSTRUCT(atup); + if (attp->attnum <= 0) /* skip system attributes for now */ + continue; + + for (i = 0; i < natts; i++) { - int i; - VacAttrStats *stats; + if (attp->attnum == vacattrstats[i].attr->attnum) + break; + } + if (i >= natts) + continue; /* skip attr if no stats collected */ + stats = &(vacattrstats[i]); - attp = (Form_pg_attribute) GETSTRUCT(atup); - if (attp->attnum <= 0) /* skip system attributes for now */ - continue; + if (VacAttrStatsEqValid(stats)) + { + float32data selratio; /* average ratio of rows selected + * for a random constant */ - for (i = 0; i < natts; i++) + /* Compute disbursion */ + if (stats->nonnull_cnt == 0 && stats->null_cnt == 0) { - if (attp->attnum == vacattrstats[i].attr->attnum) - break; - } - if (i >= natts) - continue; /* skip attr if no stats collected */ - stats = &(vacattrstats[i]); - if (VacAttrStatsEqValid(stats)) + /* + * empty relation, so put a dummy value in + * attdisbursion + */ + selratio = 0; + } + else if (stats->null_cnt <= 1 && stats->best_cnt == 1) { - float32data selratio; /* average ratio of rows selected - * for a random constant */ - - /* Compute disbursion */ - if (stats->nonnull_cnt == 0 && stats->null_cnt == 0) + /* + * looks like we have a unique-key attribute --- flag + * this with special -1.0 flag value. + * + * The correct disbursion is 1.0/numberOfRows, but since + * the relation row count can get updated without + * recomputing disbursion, we want to store a + * "symbolic" value and figure 1.0/numberOfRows on the + * fly. + */ + selratio = -1; + } + else + { + if (VacAttrStatsLtGtValid(stats) && + stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) { /* - * empty relation, so put a dummy value in - * attdisbursion + * exact result when there are just 1 or 2 + * values... */ - selratio = 0; - } - else if (stats->null_cnt <= 1 && stats->best_cnt == 1) - { + double min_cnt_d = stats->min_cnt, + max_cnt_d = stats->max_cnt, + null_cnt_d = stats->null_cnt; + double total = ((double) stats->nonnull_cnt) + null_cnt_d; - /* - * looks like we have a unique-key attribute --- flag - * this with special -1.0 flag value. - * - * The correct disbursion is 1.0/numberOfRows, but since - * the relation row count can get updated without - * recomputing disbursion, we want to store a - * "symbolic" value and figure 1.0/numberOfRows on the - * fly. - */ - selratio = -1; + selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / (total * total); } else { - if (VacAttrStatsLtGtValid(stats) && - stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) - { - - /* - * exact result when there are just 1 or 2 - * values... - */ - double min_cnt_d = stats->min_cnt, - max_cnt_d = stats->max_cnt, - null_cnt_d = stats->null_cnt; - double total = ((double) stats->nonnull_cnt) + null_cnt_d; - - selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / (total * total); - } - else - { - double most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); - double total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt); + double most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); + double total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt); - /* - * we assume count of other values are 20% of best - * count in table - */ - selratio = (most * most + 0.20 * most * (total - most)) / (total * total); - } - /* Make sure calculated values are in-range */ - if (selratio < 0.0) - selratio = 0.0; - else if (selratio > 1.0) - selratio = 1.0; + /* + * we assume count of other values are 20% of best + * count in table + */ + selratio = (most * most + 0.20 * most * (total - most)) / (total * total); } + /* Make sure calculated values are in-range */ + if (selratio < 0.0) + selratio = 0.0; + else if (selratio > 1.0) + selratio = 1.0; + } - /* overwrite the existing statistics in the tuple */ - attp->attdisbursion = selratio; + /* overwrite the existing statistics in the tuple */ + attp->attdisbursion = selratio; - /* invalidate the tuple in the cache and write the buffer */ - RelationInvalidateHeapTuple(ad, atup); - WriteNoReleaseBuffer(scan->rs_cbuf); + /* invalidate the tuple in the cache and write the buffer */ + RelationInvalidateHeapTuple(ad, atup); + WriteNoReleaseBuffer(scan->rs_cbuf); - /* - * Create pg_statistic tuples for the relation, if we have - * gathered the right data. del_stats() previously - * deleted all the pg_statistic tuples for the rel, so we - * just have to insert new ones here. + /* + * Create pg_statistic tuples for the relation, if we have + * gathered the right data. del_stats() previously + * deleted all the pg_statistic tuples for the rel, so we + * just have to insert new ones here. + * + * Note vacuum_rel() has seen to it that we won't come here + * when vacuuming pg_statistic itself. + */ + if (VacAttrStatsLtGtValid(stats) && stats->initialized) + { + float32data nullratio; + float32data bestratio; + FmgrInfo out_function; + char *out_string; + double best_cnt_d = stats->best_cnt, + null_cnt_d = stats->null_cnt, + nonnull_cnt_d = stats->nonnull_cnt; /* prevent overflow */ + Datum values[Natts_pg_statistic]; + char nulls[Natts_pg_statistic]; + + nullratio = null_cnt_d / (nonnull_cnt_d + null_cnt_d); + bestratio = best_cnt_d / (nonnull_cnt_d + null_cnt_d); + + fmgr_info(stats->outfunc, &out_function); + + for (i = 0; i < Natts_pg_statistic; ++i) + nulls[i] = ' '; + + /* ---------------- + * initialize values[] + * ---------------- + */ + i = 0; + values[i++] = (Datum) relid; /* starelid */ + values[i++] = (Datum) attp->attnum; /* staattnum */ + values[i++] = (Datum) stats->op_cmplt; /* staop */ + /* hack: this code knows float4 is pass-by-ref */ + values[i++] = PointerGetDatum(&nullratio); /* stanullfrac */ + values[i++] = PointerGetDatum(&bestratio); /* stacommonfrac */ + out_string = (*fmgr_faddr(&out_function)) (stats->best, stats->typelem, stats->attr->atttypmod); + values[i++] = PointerGetDatum(textin(out_string)); /* stacommonval */ + pfree(out_string); + out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->typelem, stats->attr->atttypmod); + values[i++] = PointerGetDatum(textin(out_string)); /* staloval */ + pfree(out_string); + out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->typelem, stats->attr->atttypmod); + values[i++] = PointerGetDatum(textin(out_string)); /* stahival */ + pfree(out_string); + + stup = heap_formtuple(sd->rd_att, values, nulls); + + /* ---------------- + * Watch out for oversize tuple, which can happen if + * all three of the saved data values are long. + * Our fallback strategy is just to not store the + * pg_statistic tuple at all in that case. (We could + * replace the values by NULLs and still store the + * numeric stats, but presently selfuncs.c couldn't + * do anything useful with that case anyway.) * - * Note vacuum_rel() has seen to it that we won't come here - * when vacuuming pg_statistic itself. + * We could reduce the probability of overflow, but not + * prevent it, by storing the data values as compressed + * text; is that worth doing? The problem should go + * away whenever long tuples get implemented... + * ---------------- */ - if (VacAttrStatsLtGtValid(stats) && stats->initialized) + if (MAXALIGN(stup->t_len) <= MaxTupleSize) { - float32data nullratio; - float32data bestratio; - FmgrInfo out_function; - char *out_string; - double best_cnt_d = stats->best_cnt, - null_cnt_d = stats->null_cnt, - nonnull_cnt_d = stats->nonnull_cnt; /* prevent overflow */ - Datum values[Natts_pg_statistic]; - char nulls[Natts_pg_statistic]; - - nullratio = null_cnt_d / (nonnull_cnt_d + null_cnt_d); - bestratio = best_cnt_d / (nonnull_cnt_d + null_cnt_d); - - fmgr_info(stats->outfunc, &out_function); - - for (i = 0; i < Natts_pg_statistic; ++i) - nulls[i] = ' '; - - /* ---------------- - * initialize values[] - * ---------------- - */ - i = 0; - values[i++] = (Datum) relid; /* starelid */ - values[i++] = (Datum) attp->attnum; /* staattnum */ - values[i++] = (Datum) stats->op_cmplt; /* staop */ - /* hack: this code knows float4 is pass-by-ref */ - values[i++] = PointerGetDatum(&nullratio); /* stanullfrac */ - values[i++] = PointerGetDatum(&bestratio); /* stacommonfrac */ - out_string = (*fmgr_faddr(&out_function)) (stats->best, stats->typelem, stats->attr->atttypmod); - values[i++] = PointerGetDatum(textin(out_string)); /* stacommonval */ - pfree(out_string); - out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->typelem, stats->attr->atttypmod); - values[i++] = PointerGetDatum(textin(out_string)); /* staloval */ - pfree(out_string); - out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->typelem, stats->attr->atttypmod); - values[i++] = PointerGetDatum(textin(out_string)); /* stahival */ - pfree(out_string); - - stup = heap_formtuple(sd->rd_att, values, nulls); - - /* ---------------- - * Watch out for oversize tuple, which can happen if - * all three of the saved data values are long. - * Our fallback strategy is just to not store the - * pg_statistic tuple at all in that case. (We could - * replace the values by NULLs and still store the - * numeric stats, but presently selfuncs.c couldn't - * do anything useful with that case anyway.) - * - * We could reduce the probability of overflow, but not - * prevent it, by storing the data values as compressed - * text; is that worth doing? The problem should go - * away whenever long tuples get implemented... - * ---------------- - */ - if (MAXALIGN(stup->t_len) <= MaxTupleSize) - { - /* OK, store tuple and update indexes too */ - Relation irelations[Num_pg_statistic_indices]; + /* OK, store tuple and update indexes too */ + Relation irelations[Num_pg_statistic_indices]; - heap_insert(sd, stup); - CatalogOpenIndices(Num_pg_statistic_indices, Name_pg_statistic_indices, irelations); - CatalogIndexInsert(irelations, Num_pg_statistic_indices, sd, stup); - CatalogCloseIndices(Num_pg_statistic_indices, irelations); - } - - /* release allocated space */ - pfree(DatumGetPointer(values[Anum_pg_statistic_stacommonval - 1])); - pfree(DatumGetPointer(values[Anum_pg_statistic_staloval - 1])); - pfree(DatumGetPointer(values[Anum_pg_statistic_stahival - 1])); - heap_freetuple(stup); + heap_insert(sd, stup); + CatalogOpenIndices(Num_pg_statistic_indices, Name_pg_statistic_indices, irelations); + CatalogIndexInsert(irelations, Num_pg_statistic_indices, sd, stup); + CatalogCloseIndices(Num_pg_statistic_indices, irelations); } + + /* release allocated space */ + pfree(DatumGetPointer(values[Anum_pg_statistic_stacommonval - 1])); + pfree(DatumGetPointer(values[Anum_pg_statistic_staloval - 1])); + pfree(DatumGetPointer(values[Anum_pg_statistic_stahival - 1])); + heap_freetuple(stup); } } - heap_endscan(scan); - /* close rels, but hold locks till upcoming commit */ - heap_close(ad, NoLock); - heap_close(sd, NoLock); } + heap_endscan(scan); + /* close rels, but hold locks till upcoming commit */ + heap_close(ad, NoLock); + heap_close(sd, NoLock); } /* @@ -2867,6 +2876,62 @@ vac_cmp_vtlinks(const void *left, const void *right) } +/* + * This routines handle a special cross-transaction portal. + * However it is automatically closed in case of abort. + */ +void +CommonSpecialPortalOpen(void) +{ + char *pname; + + + if (CommonSpecialPortalInUse) + elog(ERROR, "CommonSpecialPortal is in use"); + + /* + * Create a portal for safe memory across transactions. We need to + * palloc the name space for it because our hash function expects the + * name to be on a longword boundary. CreatePortal copies the name to + * safe storage for us. + */ + pname = pstrdup(VACPNAME); + vac_portal = CreatePortal(pname); + pfree(pname); + + /* + * Set flag to indicate that vac_portal must be removed after an error. + * This global variable is checked in the transaction manager on xact + * abort, and the routine CommonSpecialPortalClose() is called if + * necessary. + */ + CommonSpecialPortalInUse = true; +} + +void +CommonSpecialPortalClose(void) +{ + /* Clear flag first, to avoid recursion if PortalDrop elog's */ + CommonSpecialPortalInUse = false; + + /* + * Release our portal for cross-transaction memory. + */ + PortalDrop(&vac_portal); +} + +PortalVariableMemory +CommonSpecialPortalGetMemory(void) +{ + return PortalGetVariableMemory(vac_portal); +} + +bool +CommonSpecialPortalIsOpen(void) +{ + return CommonSpecialPortalInUse; +} + static void get_indices(Oid relid, int *nindices, Relation **Irel) { diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 136721c4e1..afdd0a9e6b 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: vacuum.h,v 1.27 2000/04/12 17:16:32 momjian Exp $ + * $Id: vacuum.h,v 1.28 2000/05/29 15:44:55 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -125,8 +125,6 @@ typedef struct VRelStats Size min_tlen; Size max_tlen; bool hasindex; - int va_natts; /* number of attrs being analyzed */ - VacAttrStats *vacattrstats; int num_vtlinks; VTupleLink vtlinks; } VRelStats; -- 2.40.0