1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-27 07:42:10 +03:00

Clean up API for ambulkdelete/amvacuumcleanup as per today's discussion.

This formulation requires every AM to provide amvacuumcleanup, unlike before,
but it's surely a whole lot cleaner.  Also, add an 'amstorage' column to
pg_am so that we can get rid of hardwired knowledge in DefineOpClass().
This commit is contained in:
Tom Lane
2006-05-02 22:25:10 +00:00
parent d3171dd64b
commit e57345975c
16 changed files with 334 additions and 399 deletions

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.146 2006/05/02 15:45:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.147 2006/05/02 22:25:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -339,6 +339,12 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
errmsg("cannot cluster on partial index \"%s\"",
RelationGetRelationName(OldIndex))));
if (!OldIndex->rd_am->amclusterable)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
RelationGetRelationName(OldIndex))));
if (!OldIndex->rd_am->amindexnulls)
{
AttrNumber colno;
@@ -376,12 +382,6 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
RelationGetRelationName(OldIndex))));
}
if (!OldIndex->rd_am->amclusterable)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
RelationGetRelationName(OldIndex))));
/*
* Disallow clustering system relations. This will definitely NOT work
* for shared relations (we have no way to update pg_class rows in other

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.44 2006/05/02 11:28:54 teodor Exp $
* $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.45 2006/05/02 22:25:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -77,11 +77,13 @@ DefineOpClass(CreateOpClassStmt *stmt)
opclassoid; /* oid of opclass we create */
int numOperators, /* amstrategies value */
numProcs; /* amsupport value */
bool amstorage; /* amstorage flag */
List *operators; /* OpClassMember list for operators */
List *procedures; /* OpClassMember list for support procs */
ListCell *l;
Relation rel;
HeapTuple tup;
Form_pg_am pg_am;
Datum values[Natts_pg_opclass];
char nulls[Natts_pg_opclass];
AclResult aclresult;
@@ -111,8 +113,10 @@ DefineOpClass(CreateOpClassStmt *stmt)
stmt->amname)));
amoid = HeapTupleGetOid(tup);
numOperators = ((Form_pg_am) GETSTRUCT(tup))->amstrategies;
numProcs = ((Form_pg_am) GETSTRUCT(tup))->amsupport;
pg_am = (Form_pg_am) GETSTRUCT(tup);
numOperators = pg_am->amstrategies;
numProcs = pg_am->amsupport;
amstorage = pg_am->amstorage;
/* XXX Should we make any privilege check against the AM? */
@@ -270,19 +274,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
/* Just drop the spec if same as column datatype */
if (storageoid == typeoid)
storageoid = InvalidOid;
else
{
/*
* Currently, only GiST and GIN allows storagetype different from
* datatype. This hardcoded test should be eliminated in favor of
* adding another boolean column to pg_am ...
*/
if (!(amoid == GIST_AM_OID || amoid == GIN_AM_OID))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("storage type may not be different from data type for access method \"%s\"",
stmt->amname)));
}
else if (!amstorage)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("storage type may not be different from data type for access method \"%s\"",
stmt->amname)));
}
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);

View File

@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.327 2006/05/02 11:28:54 teodor Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.328 2006/05/02 22:25:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -230,7 +230,6 @@ static void vacuum_index(VacPageList vacpagelist, Relation indrel,
double num_tuples, int keep_tuples);
static void scan_index(Relation indrel, double num_tuples);
static bool tid_reaped(ItemPointer itemptr, void *state);
static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
static void vac_update_fsm(Relation onerel, VacPageList fraged_pages,
BlockNumber rel_pages);
static VacPage copy_vac_page(VacPage vacpage);
@@ -2933,7 +2932,7 @@ vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage)
}
/*
* scan_index() -- scan one index relation to update statistic.
* scan_index() -- scan one index relation to update pg_class statistics.
*
* We use this when we have no deletions to do.
*/
@@ -2941,25 +2940,17 @@ static void
scan_index(Relation indrel, double num_tuples)
{
IndexBulkDeleteResult *stats;
IndexVacuumCleanupInfo vcinfo;
IndexVacuumInfo ivinfo;
PGRUsage ru0;
pg_rusage_init(&ru0);
/*
* Even though we're not planning to delete anything, we use the
* ambulkdelete call, because (a) the scan happens within the index AM for
* more speed, and (b) it may want to pass private statistics to the
* amvacuumcleanup call.
*/
stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
ivinfo.index = indrel;
ivinfo.vacuum_full = true;
ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = num_tuples;
/* Do post-VACUUM cleanup, even though we deleted nothing */
vcinfo.vacuum_full = true;
vcinfo.message_level = elevel;
vcinfo.num_heap_tuples = num_tuples;
stats = index_vacuum_cleanup(indrel, &vcinfo, stats);
stats = index_vacuum_cleanup(&ivinfo, NULL);
if (!stats)
return;
@@ -2982,16 +2973,7 @@ scan_index(Relation indrel, double num_tuples)
/*
* Check for tuple count mismatch. If the index is partial, then it's OK
* for it to have fewer tuples than the heap; else we got trouble.
*
* XXX Hack. Since GIN stores every pointer to heap several times and
* counting num_index_tuples during vacuum is very comlpex and slow
* we just copy num_tuples to num_index_tuples as upper limit to avoid
* WARNING and optimizer mistakes.
*/
if ( indrel->rd_rel->relam == GIN_AM_OID )
{
stats->num_index_tuples = num_tuples;
} else
if (stats->num_index_tuples != num_tuples)
{
if (stats->num_index_tuples > num_tuples ||
@@ -3023,20 +3005,21 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
double num_tuples, int keep_tuples)
{
IndexBulkDeleteResult *stats;
IndexVacuumCleanupInfo vcinfo;
IndexVacuumInfo ivinfo;
PGRUsage ru0;
pg_rusage_init(&ru0);
ivinfo.index = indrel;
ivinfo.vacuum_full = true;
ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = num_tuples + keep_tuples;
/* Do bulk deletion */
stats = index_bulk_delete(indrel, tid_reaped, (void *) vacpagelist);
stats = index_bulk_delete(&ivinfo, NULL, tid_reaped, (void *) vacpagelist);
/* Do post-VACUUM cleanup */
vcinfo.vacuum_full = true;
vcinfo.message_level = elevel;
vcinfo.num_heap_tuples = num_tuples + keep_tuples;
stats = index_vacuum_cleanup(indrel, &vcinfo, stats);
stats = index_vacuum_cleanup(&ivinfo, stats);
if (!stats)
return;
@@ -3061,16 +3044,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
/*
* Check for tuple count mismatch. If the index is partial, then it's OK
* for it to have fewer tuples than the heap; else we got trouble.
*
* XXX Hack. Since GIN stores every pointer to heap several times and
* counting num_index_tuples during vacuum is very comlpex and slow
* we just copy num_tuples to num_index_tuples as upper limit to avoid
* WARNING and optimizer mistakes.
*/
if ( indrel->rd_rel->relam == GIN_AM_OID )
{
stats->num_index_tuples = num_tuples;
} else
if (stats->num_index_tuples != num_tuples + keep_tuples)
{
if (stats->num_index_tuples > num_tuples + keep_tuples ||
@@ -3137,15 +3111,6 @@ tid_reaped(ItemPointer itemptr, void *state)
return true;
}
/*
* Dummy version for scan_index.
*/
static bool
dummy_tid_reaped(ItemPointer itemptr, void *state)
{
return false;
}
/*
* Update the shared Free Space Map with the info we now have about
* free space in the relation, discarding any old info the map may have.

View File

@@ -31,7 +31,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.69 2006/03/31 23:32:06 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.70 2006/05/02 22:25:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -96,11 +96,12 @@ static TransactionId FreezeLimit;
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, int nindexes);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
static void lazy_scan_index(Relation indrel, LVRelStats *vacrelstats);
static void lazy_vacuum_index(Relation indrel,
double *index_tups_vacuumed,
BlockNumber *index_pages_removed,
LVRelStats *vacrelstats);
IndexBulkDeleteResult **stats,
LVRelStats *vacrelstats);
static void lazy_cleanup_index(Relation indrel,
IndexBulkDeleteResult *stats,
LVRelStats *vacrelstats);
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
int tupindex, LVRelStats *vacrelstats);
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
@@ -112,7 +113,6 @@ static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
static void lazy_record_free_space(LVRelStats *vacrelstats,
BlockNumber page, Size avail);
static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
static void lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats);
static int vac_cmp_itemptr(const void *left, const void *right);
static int vac_cmp_page_spaces(const void *left, const void *right);
@@ -207,9 +207,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
tups_vacuumed,
nkeep,
nunused;
double *index_tups_vacuumed;
BlockNumber *index_pages_removed;
bool did_vacuum_index = false;
IndexBulkDeleteResult **indstats;
int i;
PGRUsage ru0;
@@ -224,15 +222,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
empty_pages = 0;
num_tuples = tups_vacuumed = nkeep = nunused = 0;
/*
* Because index vacuuming is done in multiple passes, we have to keep
* track of the total number of rows and pages removed from each index.
* index_tups_vacuumed[i] is the number removed so far from the i'th
* index. (For partial indexes this could well be different from
* tups_vacuumed.) Likewise for index_pages_removed[i].
*/
index_tups_vacuumed = (double *) palloc0(nindexes * sizeof(double));
index_pages_removed = (BlockNumber *) palloc0(nindexes * sizeof(BlockNumber));
indstats = (IndexBulkDeleteResult **)
palloc0(nindexes * sizeof(IndexBulkDeleteResult *));
nblocks = RelationGetNumberOfBlocks(onerel);
vacrelstats->rel_pages = nblocks;
@@ -263,10 +254,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
/* Remove index entries */
for (i = 0; i < nindexes; i++)
lazy_vacuum_index(Irel[i],
&index_tups_vacuumed[i],
&index_pages_removed[i],
&indstats[i],
vacrelstats);
did_vacuum_index = true;
/* Remove tuples from heap */
lazy_vacuum_heap(onerel, vacrelstats);
/* Forget the now-vacuumed tuples, and press on */
@@ -454,18 +443,15 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
/* Remove index entries */
for (i = 0; i < nindexes; i++)
lazy_vacuum_index(Irel[i],
&index_tups_vacuumed[i],
&index_pages_removed[i],
&indstats[i],
vacrelstats);
/* Remove tuples from heap */
lazy_vacuum_heap(onerel, vacrelstats);
}
else if (!did_vacuum_index)
{
/* Must do post-vacuum cleanup and statistics update anyway */
for (i = 0; i < nindexes; i++)
lazy_scan_index(Irel[i], vacrelstats);
}
/* Do post-vacuum cleanup and statistics update for each index */
for (i = 0; i < nindexes; i++)
lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
ereport(elevel,
(errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
@@ -590,94 +576,18 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
return tupindex;
}
/*
* lazy_scan_index() -- scan one index relation to update pg_class statistic.
*
* We use this when we have no deletions to do.
*/
static void
lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
{
IndexBulkDeleteResult *stats;
IndexVacuumCleanupInfo vcinfo;
PGRUsage ru0;
pg_rusage_init(&ru0);
/*
* Acquire appropriate type of lock on index: must be exclusive if index
* AM isn't concurrent-safe.
*/
if (indrel->rd_am->amconcurrent)
LockRelation(indrel, RowExclusiveLock);
else
LockRelation(indrel, AccessExclusiveLock);
/*
* Even though we're not planning to delete anything, we use the
* ambulkdelete call, because (a) the scan happens within the index AM for
* more speed, and (b) it may want to pass private statistics to the
* amvacuumcleanup call.
*/
stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
/* Do post-VACUUM cleanup, even though we deleted nothing */
vcinfo.vacuum_full = false;
vcinfo.message_level = elevel;
vcinfo.num_heap_tuples = vacrelstats->rel_tuples;
stats = index_vacuum_cleanup(indrel, &vcinfo, stats);
/*
* Release lock acquired above.
*/
if (indrel->rd_am->amconcurrent)
UnlockRelation(indrel, RowExclusiveLock);
else
UnlockRelation(indrel, AccessExclusiveLock);
if (!stats)
return;
/* now update statistics in pg_class */
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
stats->num_index_tuples,
false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
RelationGetRelationName(indrel),
stats->num_index_tuples,
stats->num_pages),
errdetail("%u index pages have been deleted, %u are currently reusable.\n"
"%s.",
stats->pages_deleted, stats->pages_free,
pg_rusage_show(&ru0))));
pfree(stats);
}
/*
* lazy_vacuum_index() -- vacuum one index relation.
*
* Delete all the index entries pointing to tuples listed in
* vacrelstats->dead_tuples.
*
* Increment *index_tups_vacuumed by the number of index entries
* removed, and *index_pages_removed by the number of pages removed.
*
* Finally, we arrange to update the index relation's statistics in
* pg_class.
* vacrelstats->dead_tuples, and update running statistics.
*/
static void
lazy_vacuum_index(Relation indrel,
double *index_tups_vacuumed,
BlockNumber *index_pages_removed,
IndexBulkDeleteResult **stats,
LVRelStats *vacrelstats)
{
IndexBulkDeleteResult *stats;
IndexVacuumCleanupInfo vcinfo;
IndexVacuumInfo ivinfo;
PGRUsage ru0;
pg_rusage_init(&ru0);
@@ -691,17 +601,59 @@ lazy_vacuum_index(Relation indrel,
else
LockRelation(indrel, AccessExclusiveLock);
/* Do bulk deletion */
stats = index_bulk_delete(indrel, lazy_tid_reaped, (void *) vacrelstats);
/* Do post-VACUUM cleanup */
vcinfo.vacuum_full = false;
vcinfo.message_level = elevel;
ivinfo.index = indrel;
ivinfo.vacuum_full = false;
ivinfo.message_level = elevel;
/* We don't yet know rel_tuples, so pass -1 */
/* index_bulk_delete can't have skipped scan anyway ... */
vcinfo.num_heap_tuples = -1;
ivinfo.num_heap_tuples = -1;
stats = index_vacuum_cleanup(indrel, &vcinfo, stats);
/* Do bulk deletion */
*stats = index_bulk_delete(&ivinfo, *stats,
lazy_tid_reaped, (void *) vacrelstats);
/*
* Release lock acquired above.
*/
if (indrel->rd_am->amconcurrent)
UnlockRelation(indrel, RowExclusiveLock);
else
UnlockRelation(indrel, AccessExclusiveLock);
ereport(elevel,
(errmsg("scanned index \"%s\" to remove %d row versions",
RelationGetRelationName(indrel),
vacrelstats->num_dead_tuples),
errdetail("%s.", pg_rusage_show(&ru0))));
}
/*
* lazy_cleanup_index() -- do post-vacuum cleanup for one index relation.
*/
static void
lazy_cleanup_index(Relation indrel,
IndexBulkDeleteResult *stats,
LVRelStats *vacrelstats)
{
IndexVacuumInfo ivinfo;
PGRUsage ru0;
pg_rusage_init(&ru0);
/*
* Acquire appropriate type of lock on index: must be exclusive if index
* AM isn't concurrent-safe.
*/
if (indrel->rd_am->amconcurrent)
LockRelation(indrel, RowExclusiveLock);
else
LockRelation(indrel, AccessExclusiveLock);
ivinfo.index = indrel;
ivinfo.vacuum_full = false;
ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = vacrelstats->rel_tuples;
stats = index_vacuum_cleanup(&ivinfo, stats);
/*
* Release lock acquired above.
@@ -714,10 +666,6 @@ lazy_vacuum_index(Relation indrel,
if (!stats)
return;
/* accumulate total removed over multiple index-cleaning cycles */
*index_tups_vacuumed += stats->tuples_removed;
*index_pages_removed += stats->pages_removed;
/* now update statistics in pg_class */
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
@@ -1134,15 +1082,6 @@ lazy_tid_reaped(ItemPointer itemptr, void *state)
return (res != NULL);
}
/*
* Dummy version for lazy_scan_index.
*/
static bool
dummy_tid_reaped(ItemPointer itemptr, void *state)
{
return false;
}
/*
* Update the shared Free Space Map with the info we now have about
* free space in the relation, discarding any old info the map may have.