77 *
88 *
99 * IDENTIFICATION
10- * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.127 1999/11/28 02:10:01 tgl Exp $
10+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.128 1999/11/29 04:43:15 tgl Exp $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
3535#include "storage/sinval.h"
3636#include "storage/smgr.h"
3737#include "tcop/tcopprot.h"
38+ #include "utils/acl.h"
3839#include "utils/builtins.h"
3940#include "utils/inval.h"
4041#include "utils/portal.h"
@@ -393,15 +394,35 @@ vc_vacone(Oid relid, bool analyze, List *va_cols)
393394}
394395
395396/*
396- * Open the class, get an exclusive lock on it, and vacuum it
397+ * Open the class, get an exclusive lock on it, and check permissions.
398+ *
399+ * Note we choose to treat permissions failure as a NOTICE and keep
400+ * trying to vacuum the rest of the DB --- is this appropriate?
397401 */
398402onerel = heap_open (relid ,AccessExclusiveLock );
399403
404+ #ifndef NO_SECURITY
405+ if (!pg_ownercheck (GetPgUserName (),RelationGetRelationName (onerel ),
406+ RELNAME ))
407+ {
408+ elog (NOTICE ,"Skipping \"%s\" --- only table owner can VACUUM it" ,
409+ RelationGetRelationName (onerel ));
410+ heap_close (onerel ,AccessExclusiveLock );
411+ CommitTransactionCommand ();
412+ return ;
413+ }
414+ #endif
415+
416+ /*
417+ * Set up statistics-gathering machinery.
418+ */
400419vacrelstats = (VRelStats * )palloc (sizeof (VRelStats ));
401420vacrelstats -> relid = relid ;
402421vacrelstats -> num_pages = vacrelstats -> num_tuples = 0 ;
403422vacrelstats -> hasindex = false;
404- if (analyze && !IsSystemRelationName (RelationGetRelationName (onerel )))
423+ /* we can VACUUM ANALYZE any table except pg_statistic; see vc_updstats */
424+ if (analyze &&
425+ strcmp (RelationGetRelationName (onerel ),StatisticRelationName )!= 0 )
405426{
406427int attr_cnt ,
407428* attnums = NULL ;
@@ -498,7 +519,7 @@ vc_vacone(Oid relid, bool analyze, List *va_cols)
498519stats -> outfunc = InvalidOid ;
499520}
500521vacrelstats -> va_natts = attr_cnt ;
501- /* delete existingpg_statistics rows for relation */
522+ /* delete existingpg_statistic rows for relation */
502523vc_delstats (relid , ((attnums ) ?attr_cnt :0 ),attnums );
503524if (attnums )
504525pfree (attnums );
@@ -2248,19 +2269,38 @@ vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len
22482269}
22492270
22502271/*
2251- *vc_updstats() -- update pg_class statistics for one relation
2272+ *vc_updstats() -- update statistics for one relation
2273+ *
2274+ *Statistics are stored in several places: the pg_class row for the
2275+ *relation has stats about the whole relation, the pg_attribute rows
2276+ *for each attribute store "disbursion", and there is a pg_statistic
2277+ *row for each (non-system) attribute. (Disbursion probably ought to
2278+ *be moved to pg_statistic, but it's not worth doing unless there's
2279+ *another reason to have to change pg_attribute.) Disbursion and
2280+ *pg_statistic values are only updated by VACUUM ANALYZE, but we
2281+ *always update the stats in pg_class.
22522282 *
22532283 *This routine works for both index and heap relation entries in
22542284 *pg_class. We violate no-overwrite semantics here by storing new
2255- *values fornum_tuples, num_pages, and hasindex directlyin the pg_class
2285+ *values forthe statistics columns directlyinto the pg_class
22562286 *tuple that's already on the page. The reason for this is that if
2257- *we updated these tuples in the usual way, then every tuple in pg_class
2258- *would be replaced every day. This would make planning and executing
2259- *historical queries very expensive. Note that we also don't use
2260- *any locking while doing updation.
2287+ *we updated these tuples in the usual way, vacuuming pg_class itself
2288+ *wouldn't work very well --- by the time we got done with a vacuum
2289+ *cycle, most of the tuples in pg_class would've been obsoleted.
2290+ *Updating pg_class's own statistics would be especially tricky.
2291+ *Of course, this only works for fixed-size never-null columns, but
2292+ *these are.
2293+ *
2294+ *Updates of pg_attribute statistics are handled in the same way
2295+ *for the same reasons.
2296+ *
2297+ *To keep things simple, we punt for pg_statistic, and don't try
2298+ *to compute or store rows for pg_statistic itself in pg_statistic.
2299+ *This could possibly be made to work, but it's not worth the trouble.
22612300 */
22622301static void
2263- vc_updstats (Oid relid ,int num_pages ,int num_tuples ,bool hasindex ,VRelStats * vacrelstats )
2302+ vc_updstats (Oid relid ,int num_pages ,int num_tuples ,bool hasindex ,
2303+ VRelStats * vacrelstats )
22642304{
22652305Relation rd ,
22662306ad ,
@@ -2298,13 +2338,21 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
22982338pgcform -> relpages = num_pages ;
22992339pgcform -> relhasindex = hasindex ;
23002340
2341+ /* invalidate the tuple in the cache and write the buffer */
2342+ RelationInvalidateHeapTuple (rd ,& rtup );
2343+ WriteBuffer (buffer );
2344+
2345+ heap_close (rd ,RowExclusiveLock );
2346+
23012347if (vacrelstats != NULL && vacrelstats -> va_natts > 0 )
23022348{
23032349VacAttrStats * vacattrstats = vacrelstats -> vacattrstats ;
23042350int natts = vacrelstats -> va_natts ;
23052351
23062352ad = heap_openr (AttributeRelationName ,RowExclusiveLock );
23072353sd = heap_openr (StatisticRelationName ,RowExclusiveLock );
2354+
2355+ /* Find pg_attribute rows for this relation */
23082356ScanKeyEntryInitialize (& askey ,0 ,Anum_pg_attribute_attrelid ,
23092357F_INT4EQ ,relid );
23102358
@@ -2313,15 +2361,10 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
23132361while (HeapTupleIsValid (atup = heap_getnext (scan ,0 )))
23142362{
23152363int i ;
2316- float32data selratio ;/* average ratio of rows selected
2317- * for a random constant */
23182364VacAttrStats * stats ;
2319- Datum values [Natts_pg_statistic ];
2320- char nulls [Natts_pg_statistic ];
23212365
23222366attp = (Form_pg_attribute )GETSTRUCT (atup );
2323- if (attp -> attnum <=0 )/* skip system attributes for now, */
2324- /* they are unique anyway */
2367+ if (attp -> attnum <=0 )/* skip system attributes for now */
23252368continue ;
23262369
23272370for (i = 0 ;i < natts ;i ++ )
@@ -2330,12 +2373,15 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
23302373break ;
23312374}
23322375if (i >=natts )
2333- continue ;
2376+ continue ;/* skip attr if no stats collected */
23342377stats = & (vacattrstats [i ]);
23352378
2336- /* overwrite the existing statistics in the tuple */
23372379if (VacAttrStatsEqValid (stats ))
23382380{
2381+ float32data selratio ;/* average ratio of rows selected
2382+ * for a random constant */
2383+
2384+ /* Compute disbursion */
23392385if (stats -> nonnull_cnt == 0 && stats -> null_cnt == 0 )
23402386{
23412387/* empty relation, so put a dummy value in attdisbursion */
@@ -2383,23 +2429,24 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
23832429else if (selratio > 1.0 )
23842430selratio = 1.0 ;
23852431}
2432+
2433+ /* overwrite the existing statistics in the tuple */
23862434attp -> attdisbursion = selratio ;
23872435
2388- /*
2389- * Invalidate the cache for the tuple and write the buffer
2390- */
2436+ /* invalidate the tuple in the cache and write the buffer */
23912437RelationInvalidateHeapTuple (ad ,atup );
23922438WriteNoReleaseBuffer (scan -> rs_cbuf );
23932439
2394- /* DO PG_STATISTIC INSERTS */
2395-
23962440/*
2397- * doing system relations, especially pg_statistic is a
2398- * problem
2441+ * Create pg_statistic tuples for the relation, if we have
2442+ * gathered the right data. vc_delstats() previously
2443+ * deleted all the pg_statistic tuples for the rel, so we
2444+ * just have to insert new ones here.
2445+ *
2446+ * Note vc_vacone() has seen to it that we won't come here
2447+ * when vacuuming pg_statistic itself.
23992448 */
2400- if (VacAttrStatsLtGtValid (stats )&& stats -> initialized
2401- /* && !IsSystemRelationName(NameData(pgcform->relname))
2402- */ )
2449+ if (VacAttrStatsLtGtValid (stats )&& stats -> initialized )
24032450{
24042451float32data nullratio ;
24052452float32data bestratio ;
@@ -2408,6 +2455,8 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
24082455double best_cnt_d = stats -> best_cnt ,
24092456null_cnt_d = stats -> null_cnt ,
24102457nonnull_cnt_d = stats -> nonnull_cnt ;/* prevent overflow */
2458+ Datum values [Natts_pg_statistic ];
2459+ char nulls [Natts_pg_statistic ];
24112460
24122461nullratio = null_cnt_d / (nonnull_cnt_d + null_cnt_d );
24132462bestratio = best_cnt_d / (nonnull_cnt_d + null_cnt_d );
@@ -2441,13 +2490,26 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
24412490stup = heap_formtuple (sd -> rd_att ,values ,nulls );
24422491
24432492/* ----------------
2444- *insert the tuple in the relation.
2493+ *Watch out for oversize tuple, which can happen if
2494+ *all three of the saved data values are long.
2495+ *Our fallback strategy is just to not store the
2496+ *pg_statistic tuple at all in that case. (We could
2497+ *replace the values by NULLs and still store the
2498+ *numeric stats, but presently selfuncs.c couldn't
2499+ *do anything useful with that case anyway.)
2500+ *
2501+ *We could reduce the probability of overflow, but not
2502+ *prevent it, by storing the data values as compressed
2503+ *text; is that worth doing? The problem should go
2504+ *away whenever long tuples get implemented...
24452505 * ----------------
24462506 */
2447- heap_insert (sd ,stup );
2448-
2507+ if (MAXALIGN (stup -> t_len ) <=MaxTupleSize )
24492508{
2509+ /* OK, store tuple and update indexes too */
24502510Relation irelations [Num_pg_statistic_indices ];
2511+
2512+ heap_insert (sd ,stup );
24512513CatalogOpenIndices (Num_pg_statistic_indices ,Name_pg_statistic_indices ,irelations );
24522514CatalogIndexInsert (irelations ,Num_pg_statistic_indices ,sd ,stup );
24532515CatalogCloseIndices (Num_pg_statistic_indices ,irelations );
@@ -2462,22 +2524,14 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
24622524}
24632525}
24642526heap_endscan (scan );
2465- heap_close (ad ,RowExclusiveLock );
2466- heap_close (sd ,RowExclusiveLock );
2527+ /* close rels, but hold locks till upcoming commit */
2528+ heap_close (ad ,NoLock );
2529+ heap_close (sd ,NoLock );
24672530}
2468-
2469- /*
2470- * Invalidate the cached pg_class tuple and write the buffer
2471- */
2472- RelationInvalidateHeapTuple (rd ,& rtup );
2473-
2474- WriteBuffer (buffer );
2475-
2476- heap_close (rd ,RowExclusiveLock );
24772531}
24782532
24792533/*
2480- *vc_delstats() -- deletepg_statistics rows for a relation
2534+ *vc_delstats() -- deletepg_statistic rows for a relation
24812535 *
24822536 *If a list of attribute numbers is given, only zap stats for those attrs.
24832537 */
@@ -2514,7 +2568,13 @@ vc_delstats(Oid relid, int attcnt, int *attnums)
25142568}
25152569
25162570heap_endscan (scan );
2517- heap_close (pgstatistic ,RowExclusiveLock );
2571+ /*
2572+ * Close rel, but *keep* lock; we will need to reacquire it later,
2573+ * so there's a possibility of deadlock against another VACUUM process
2574+ * if we let go now. Keeping the lock shouldn't delay any common
2575+ * operation other than an attempted VACUUM of pg_statistic itself.
2576+ */
2577+ heap_close (pgstatistic ,NoLock );
25182578}
25192579
25202580/*