OSDN Git Service

Allow vacuum to perform analyze with shared lock. Update cvs manual.
authorBruce Momjian <bruce@momjian.us>
Mon, 29 May 2000 15:44:55 +0000 (15:44 +0000)
committerBruce Momjian <bruce@momjian.us>
Mon, 29 May 2000 15:44:55 +0000 (15:44 +0000)
doc/src/sgml/cvs.sgml
src/backend/commands/vacuum.c
src/include/commands/vacuum.h

index 50f9a0c..bf2ee60 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/cvs.sgml,v 1.8 2000/05/02 20:01:51 thomas Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/cvs.sgml,v 1.9 2000/05/29 15:44:54 momjian Exp $
 CVS code repository
 Thomas Lockhart
 -->
@@ -184,7 +184,7 @@ cvs commit
      Do an initial login to the <productname>CVS</productname> server:
 
      <programlisting>
-$ cvs -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot login
+$ cvs -d :pserver:anoncvs@postgresql.org:/home/projects/pgsql/cvsroot login
      </programlisting>
 
      You will be prompted for a password; enter '<literal>postgresql</literal>'.
@@ -197,7 +197,7 @@ $ cvs -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot login
     <para>
      Fetch the <productname>Postgres</productname> sources:
      <programlisting>
-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
      </programlisting>
 
      which installs the <productname>Postgres</productname> sources into a 
index 3160e20..bd8386b 100644 (file)
@@ -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,18 +493,198 @@ 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
  *
  *             This routine sets commit times, constructs vacuum_pages list of
@@ -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)
 {
index 136721c..afdd0a9 100644 (file)
@@ -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;