提交 946cf229 编写于 作者: I Itagaki Takahiro

Support rewritten-based full vacuum as VACUUM FULL. Traditional

VACUUM FULL was renamed to VACUUM FULL INPLACE. Also added a new
option -i, --inplace for vacuumdb to perform FULL INPLACE vacuuming.

Since the new VACUUM FULL uses CLUSTER infrastructure, we cannot
use it for system tables. VACUUM FULL for system tables always
fall back into VACUUM FULL INPLACE silently.

Itagaki Takahiro, reviewed by Jeff Davis and Simon Riggs.
上级 28f6cab6
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.56 2009/11/16 21:32:06 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.57 2010/01/06 05:31:13 itagaki Exp $
PostgreSQL documentation
-->
......@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
VACUUM [ ( { FULL [ INPLACE ] | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
</synopsis>
......@@ -86,6 +86,27 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
Selects <quote>full</quote> vacuum, which can reclaim more
space, but takes much longer and exclusively locks the table.
</para>
<para>
For user tables, all table data and indexes are rewritten. This
method requires extra disk space in which to write the new data,
and is generally useful when a significant amount of space needs
to be reclaimed from within the table.
</para>
<para>
For system tables, all table data and indexes are modified in
place to reclaim space. This method may require less disk space
for the table data than <command>VACUUM FULL</command> on a
comparable user table, but the indexes will grow which may
counteract that benefit. Additionally, the operation is often
slower than <command>VACUUM FULL</command> on a comparable user
table.
</para>
<para>
If <literal>FULL INPLACE</literal> is specified, the space is
reclaimed in the same manner as a system table, even if it is a
user table. Specifying <literal>INPLACE</literal> explicitly is
rarely useful.
</para>
</listitem>
</varlistentry>
......
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuumdb.sgml,v 1.46 2010/01/06 02:59:44 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuumdb.sgml,v 1.47 2010/01/06 05:31:13 itagaki Exp $
PostgreSQL documentation
-->
......@@ -24,6 +24,7 @@ PostgreSQL documentation
<command>vacuumdb</command>
<arg rep="repeat"><replaceable>connection-option</replaceable></arg>
<group><arg>--full</arg><arg>-f</arg></group>
<group><arg>--inplace</arg><arg>-i</arg></group>
<group><arg>--freeze</arg><arg>-F</arg></group>
<group><arg>--verbose</arg><arg>-v</arg></group>
<group><arg>--analyze</arg><arg>-z</arg></group>
......@@ -37,6 +38,7 @@ PostgreSQL documentation
<arg rep="repeat"><replaceable>connection-options</replaceable></arg>
<group><arg>--all</arg><arg>-a</arg></group>
<group><arg>--full</arg><arg>-f</arg></group>
<group><arg>--inplace</arg><arg>-i</arg></group>
<group><arg>--freeze</arg><arg>-F</arg></group>
<group><arg>--verbose</arg><arg>-v</arg></group>
<group><arg>--analyze</arg><arg>-z</arg></group>
......@@ -129,6 +131,16 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>-i</option></term>
<term><option>--inplace</option></term>
<listitem>
<para>
Perform <quote>full inplace</quote> vacuuming.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-o</option></term>
<term><option>--only-analyze</option></term>
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.190 2010/01/06 03:04:00 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.191 2010/01/06 05:31:13 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -61,9 +61,10 @@ typedef struct
} RelToCluster;
static void cluster_rel(RelToCluster *rv, bool recheck, bool verbose);
static void rebuild_relation(Relation OldHeap, Oid indexOid);
static TransactionId copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
static void rebuild_relation(Relation OldHeap, Oid indexOid,
int freeze_min_age, int freeze_table_age);
static TransactionId copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap,
Oid OIDOldIndex, int freeze_min_age, int freeze_table_age);
static List *get_tables_to_cluster(MemoryContext cluster_context);
......@@ -101,7 +102,6 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
Oid tableOid,
indexOid = InvalidOid;
Relation rel;
RelToCluster rvtc;
/* Find and lock the table */
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
......@@ -169,15 +169,11 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
stmt->indexname, stmt->relation->relname)));
}
/* All other checks are done in cluster_rel() */
rvtc.tableOid = tableOid;
rvtc.indexOid = indexOid;
/* close relation, keep lock till commit */
heap_close(rel, NoLock);
/* Do the job */
cluster_rel(&rvtc, false, stmt->verbose);
cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1);
}
else
{
......@@ -226,7 +222,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
cluster_rel(rvtc, true, stmt->verbose);
cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose, -1, -1);
PopActiveSnapshot();
CommitTransactionCommand();
}
......@@ -252,9 +248,13 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
* same way we do for the relation. Since we are effectively bulk-loading
* the new table, it's better to create the indexes afterwards than to fill
* them incrementally while we load the table.
*
* If indexOid is InvalidOid, the table will be rewritten in physical order
* instead of index order.
*/
static void
cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
void
cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
int freeze_min_age, int freeze_table_age)
{
Relation OldHeap;
......@@ -267,7 +267,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
* case, since cluster() already did it.) The index lock is taken inside
* check_index_is_clusterable.
*/
OldHeap = try_relation_open(rvtc->tableOid, AccessExclusiveLock);
OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
/* If the table has gone away, we can skip processing it */
if (!OldHeap)
......@@ -287,7 +287,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
Form_pg_index indexForm;
/* Check that the user still owns the relation */
if (!pg_class_ownercheck(rvtc->tableOid, GetUserId()))
if (!pg_class_ownercheck(tableOid, GetUserId()))
{
relation_close(OldHeap, AccessExclusiveLock);
return;
......@@ -308,11 +308,13 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
return;
}
if (OidIsValid(indexOid))
{
/*
* Check that the index still exists
*/
if (!SearchSysCacheExists(RELOID,
ObjectIdGetDatum(rvtc->indexOid),
ObjectIdGetDatum(indexOid),
0, 0, 0))
{
relation_close(OldHeap, AccessExclusiveLock);
......@@ -323,7 +325,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
* Check that the index is still the one with indisclustered set.
*/
tuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(rvtc->indexOid),
ObjectIdGetDatum(indexOid),
0, 0, 0);
if (!HeapTupleIsValid(tuple)) /* probably can't happen */
{
......@@ -339,22 +341,29 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
}
ReleaseSysCache(tuple);
}
}
/* Check index is valid to cluster on */
check_index_is_clusterable(OldHeap, rvtc->indexOid, recheck);
/* Check heap and index are valid to cluster on */
check_index_is_clusterable(OldHeap, indexOid, recheck);
/* rebuild_relation does all the dirty work */
if (OidIsValid(indexOid))
ereport(verbose ? INFO : DEBUG2,
(errmsg("clustering \"%s.%s\"",
get_namespace_name(RelationGetNamespace(OldHeap)),
RelationGetRelationName(OldHeap))));
rebuild_relation(OldHeap, rvtc->indexOid);
else
ereport(verbose ? INFO : DEBUG2,
(errmsg("vacuuming \"%s.%s\"",
get_namespace_name(RelationGetNamespace(OldHeap)),
RelationGetRelationName(OldHeap))));
rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age);
/* NB: rebuild_relation does heap_close() on OldHeap */
}
/*
* Verify that the specified index is a legitimate index to cluster on
* Verify that the specified heap and index are valid to cluster on
*
* Side effect: obtains exclusive lock on the index. The caller should
* already have exclusive lock on the table, so the index lock is likely
......@@ -366,6 +375,38 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
{
Relation OldIndex;
/*
* Disallow clustering system relations. This will definitely NOT work
* for shared relations (we have no way to update pg_class rows in other
* databases), nor for nailed-in-cache relations (the relfilenode values
* for those are hardwired, see relcache.c). It might work for other
* system relations, but I ain't gonna risk it.
*/
if (IsSystemRelation(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"%s\" is a system catalog",
RelationGetRelationName(OldHeap))));
/*
* Don't allow cluster on temp tables of other backends ... their local
* buffer manager is not going to cope.
*/
if (RELATION_IS_OTHER_TEMP(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster temporary tables of other sessions")));
/*
* Also check for active uses of the relation in the current transaction,
* including open scans and pending AFTER trigger events.
*/
CheckTableNotInUse(OldHeap, "CLUSTER");
/* Skip checks for index if not specified. */
if (!OidIsValid(indexOid))
return;
OldIndex = index_open(indexOid, AccessExclusiveLock);
/*
......@@ -448,34 +489,6 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
errmsg("cannot cluster on invalid index \"%s\"",
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
* databases), nor for nailed-in-cache relations (the relfilenode values
* for those are hardwired, see relcache.c). It might work for other
* system relations, but I ain't gonna risk it.
*/
if (IsSystemRelation(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"%s\" is a system catalog",
RelationGetRelationName(OldHeap))));
/*
* Don't allow cluster on temp tables of other backends ... their local
* buffer manager is not going to cope.
*/
if (RELATION_IS_OTHER_TEMP(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster temporary tables of other sessions")));
/*
* Also check for active uses of the relation in the current transaction,
* including open scans and pending AFTER trigger events.
*/
CheckTableNotInUse(OldHeap, "CLUSTER");
/* Drop relcache refcnt on OldIndex, but keep lock */
index_close(OldIndex, NoLock);
}
......@@ -557,15 +570,16 @@ mark_index_clustered(Relation rel, Oid indexOid)
}
/*
* rebuild_relation: rebuild an existing relation in index order
* rebuild_relation: rebuild an existing relation in index or physical order
*
* OldHeap: table to rebuild --- must be opened and exclusive-locked!
* indexOid: index to cluster by
* indexOid: index to cluster by, or InvalidOid to rewrite in physical order.
*
* NB: this routine closes OldHeap at the right time; caller should not.
*/
static void
rebuild_relation(Relation OldHeap, Oid indexOid)
rebuild_relation(Relation OldHeap, Oid indexOid,
int freeze_min_age, int freeze_table_age)
{
Oid tableOid = RelationGetRelid(OldHeap);
Oid tableSpace = OldHeap->rd_rel->reltablespace;
......@@ -576,6 +590,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
Relation newrel;
/* Mark the correct index as clustered */
if (OidIsValid(indexOid))
mark_index_clustered(OldHeap, indexOid);
/* Close relcache entry, but keep lock until transaction commit */
......@@ -599,7 +614,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
/*
* Copy the heap data into the new table in the desired order.
*/
frozenXid = copy_heap_data(OIDNewHeap, tableOid, indexOid);
frozenXid = copy_heap_data(OIDNewHeap, tableOid, indexOid,
freeze_min_age, freeze_table_age);
/* To make the new heap's data visible (probably not needed?). */
CommandCounterIncrement();
......@@ -758,7 +774,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
* freeze cutoff point for the tuples.
*/
static TransactionId
copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
int freeze_min_age, int freeze_table_age)
{
Relation NewHeap,
OldHeap,
......@@ -768,8 +785,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
int natts;
Datum *values;
bool *isnull;
IndexScanDesc scan;
HeapTuple tuple;
IndexScanDesc indexScan;
HeapScanDesc heapScan;
bool use_wal;
TransactionId OldestXmin;
TransactionId FreezeXid;
......@@ -780,7 +797,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
*/
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
if (OidIsValid(OIDOldIndex))
OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
else
OldIndex = NULL;
/*
* Their tuple descriptors should be exactly alike, but here we only need
......@@ -809,8 +829,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* freeze_min_age to avoid having CLUSTER freeze tuples earlier than a
* plain VACUUM would.
*/
vacuum_set_xid_limits(-1, -1, OldHeap->rd_rel->relisshared,
&OldestXmin, &FreezeXid, NULL);
vacuum_set_xid_limits(freeze_min_age, freeze_table_age,
OldHeap->rd_rel->relisshared, &OldestXmin, &FreezeXid, NULL);
/*
* FreezeXid will become the table's new relfrozenxid, and that mustn't go
......@@ -828,25 +848,46 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* copied, we scan with SnapshotAny and use HeapTupleSatisfiesVacuum for
* the visibility test.
*/
scan = index_beginscan(OldHeap, OldIndex,
if (OldIndex != NULL)
indexScan = index_beginscan(OldHeap, OldIndex,
SnapshotAny, 0, (ScanKey) NULL);
else
heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
for (;;)
{
HeapTuple tuple;
HeapTuple copiedTuple;
Buffer buf;
bool isdead;
int i;
CHECK_FOR_INTERRUPTS();
if (OldIndex != NULL)
{
tuple = index_getnext(indexScan, ForwardScanDirection);
if (tuple == NULL)
break;
/* Since we used no scan keys, should never need to recheck */
if (scan->xs_recheck)
if (indexScan->xs_recheck)
elog(ERROR, "CLUSTER does not support lossy index conditions");
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
buf = indexScan->xs_cbuf;
}
else
{
tuple = heap_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
buf = heapScan->rs_cbuf;
}
switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
scan->xs_cbuf))
LockBuffer(buf, BUFFER_LOCK_SHARE);
switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
......@@ -888,7 +929,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
break;
}
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (isdead)
{
......@@ -932,7 +973,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
heap_freetuple(copiedTuple);
}
index_endscan(scan);
if (OldIndex != NULL)
index_endscan(indexScan);
else
heap_endscan(heapScan);
/* Write out any remaining tuples, and fsync if needed */
end_heap_rewrite(rwstate);
......@@ -940,6 +984,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
pfree(values);
pfree(isnull);
if (OldIndex != NULL)
index_close(OldIndex, NoLock);
heap_close(OldHeap, NoLock);
heap_close(NewHeap, NoLock);
......
......@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.402 2010/01/02 16:57:40 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.403 2010/01/06 05:31:13 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -29,10 +29,12 @@
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/storage.h"
#include "commands/cluster.h"
#include "commands/dbcommands.h"
#include "commands/vacuum.h"
#include "executor/executor.h"
......@@ -302,6 +304,8 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
Assert((vacstmt->options & VACOPT_VACUUM) ||
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
Assert((vacstmt->options & VACOPT_ANALYZE) || vacstmt->va_cols == NIL);
Assert((vacstmt->options & VACOPT_FULL) ||
!(vacstmt->options & VACOPT_INPLACE));
stmttype = (vacstmt->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
......@@ -1178,12 +1182,24 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
save_nestlevel = NewGUCNestLevel();
/*
* Do the actual work --- either FULL or "lazy" vacuum
* Do the actual work --- either FULL, FULL INPLACE, or "lazy" vacuum.
* We can use only FULL INPLACE vacuum for system relations.
*/
if (vacstmt->options & VACOPT_FULL)
if (!(vacstmt->options & VACOPT_FULL))
heldoff = lazy_vacuum_rel(onerel, vacstmt, vac_strategy, scanned_all);
else if ((vacstmt->options & VACOPT_INPLACE) || IsSystemRelation(onerel))
heldoff = full_vacuum_rel(onerel, vacstmt);
else
heldoff = lazy_vacuum_rel(onerel, vacstmt, vac_strategy, scanned_all);
{
/* close relation before clustering, but hold lock until commit */
relation_close(onerel, NoLock);
onerel = NULL;
cluster_rel(relid, InvalidOid, false,
(vacstmt->options & VACOPT_VERBOSE) != 0,
vacstmt->freeze_min_age, vacstmt->freeze_table_age);
heldoff = false;
}
/* Roll back any GUC changes executed by index functions */
AtEOXact_GUC(false, save_nestlevel);
......@@ -1192,6 +1208,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
SetUserIdAndSecContext(save_userid, save_sec_context);
/* all done with this class, but hold lock until commit */
if (onerel)
relation_close(onerel, NoLock);
/*
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.702 2010/01/05 21:53:58 rhaas Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.703 2010/01/06 05:31:13 itagaki Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -490,7 +490,7 @@ static TypeName *TableFuncTypeName(List *columns);
IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
INNER_P INOUT INPLACE INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
JOIN
......@@ -6791,6 +6791,7 @@ vacuum_option_elem:
| VERBOSE { $$ = VACOPT_VERBOSE; }
| FREEZE { $$ = VACOPT_FREEZE; }
| FULL { $$ = VACOPT_FULL; }
| FULL INPLACE { $$ = VACOPT_FULL | VACOPT_INPLACE; }
;
AnalyzeStmt:
......@@ -10781,6 +10782,7 @@ unreserved_keyword:
| INHERIT
| INHERITS
| INLINE_P
| INPLACE
| INPUT_P
| INSENSITIVE
| INSERT
......
......@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/bin/scripts/vacuumdb.c,v 1.29 2010/01/06 02:59:46 momjian Exp $
* $PostgreSQL: pgsql/src/bin/scripts/vacuumdb.c,v 1.30 2010/01/06 05:31:14 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -14,12 +14,12 @@
#include "common.h"
static void vacuum_one_database(const char *dbname, bool full, bool verbose,
static void vacuum_one_database(const char *dbname, bool full, bool inplace, bool verbose,
bool and_analyze, bool only_analyze, bool freeze,
const char *table, const char *host, const char *port,
const char *username, enum trivalue prompt_password,
const char *progname, bool echo);
static void vacuum_all_databases(bool full, bool verbose, bool and_analyze,
static void vacuum_all_databases(bool full, bool inplace, bool verbose, bool and_analyze,
bool only_analyze, bool freeze,
const char *host, const char *port,
const char *username, enum trivalue prompt_password,
......@@ -47,6 +47,7 @@ main(int argc, char *argv[])
{"table", required_argument, NULL, 't'},
{"full", no_argument, NULL, 'f'},
{"verbose", no_argument, NULL, 'v'},
{"inplace", no_argument, NULL, 'i'},
{NULL, 0, NULL, 0}
};
......@@ -68,13 +69,14 @@ main(int argc, char *argv[])
char *table = NULL;
bool full = false;
bool verbose = false;
bool inplace = false;
progname = get_progname(argv[0]);
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
handle_help_version_opts(argc, argv, "vacuumdb", help);
while ((c = getopt_long(argc, argv, "h:p:U:wWeqd:zaFt:fv", long_options, &optindex)) != -1)
while ((c = getopt_long(argc, argv, "h:p:U:wWeqd:zaFt:fiv", long_options, &optindex)) != -1)
{
switch (c)
{
......@@ -120,6 +122,9 @@ main(int argc, char *argv[])
case 'f':
full = true;
break;
case 'i':
inplace = true;
break;
case 'v':
verbose = true;
break;
......@@ -143,7 +148,12 @@ main(int argc, char *argv[])
exit(1);
}
setup_cancel_handler();
if (inplace && !full)
{
fprintf(stderr, _("%s: cannot use the \"inplace\" option when performing full vacuum\n"),
progname);
exit(1);
}
if (only_analyze)
{
......@@ -162,6 +172,8 @@ main(int argc, char *argv[])
/* ignore 'and_analyze' */
}
setup_cancel_handler();
if (alldb)
{
if (dbname)
......@@ -177,7 +189,7 @@ main(int argc, char *argv[])
exit(1);
}
vacuum_all_databases(full, verbose, and_analyze, only_analyze, freeze,
vacuum_all_databases(full, inplace, verbose, and_analyze, only_analyze, freeze,
host, port, username, prompt_password,
progname, echo, quiet);
}
......@@ -193,7 +205,7 @@ main(int argc, char *argv[])
dbname = get_user_name(progname);
}
vacuum_one_database(dbname, full, verbose, and_analyze, only_analyze,
vacuum_one_database(dbname, full, inplace, verbose, and_analyze, only_analyze,
freeze, table,
host, port, username, prompt_password,
progname, echo);
......@@ -204,7 +216,7 @@ main(int argc, char *argv[])
static void
vacuum_one_database(const char *dbname, bool full, bool verbose, bool and_analyze,
vacuum_one_database(const char *dbname, bool full, bool inplace, bool verbose, bool and_analyze,
bool only_analyze, bool freeze, const char *table,
const char *host, const char *port,
const char *username, enum trivalue prompt_password,
......@@ -216,25 +228,67 @@ vacuum_one_database(const char *dbname, bool full, bool verbose, bool and_analyz
initPQExpBuffer(&sql);
conn = connectDatabase(dbname, host, port, username, prompt_password, progname);
if (only_analyze)
{
appendPQExpBuffer(&sql, "ANALYZE");
if (verbose)
appendPQExpBuffer(&sql, " VERBOSE");
}
else
{
appendPQExpBuffer(&sql, "VACUUM");
if (PQserverVersion(conn) >= 80500)
{
const char *paren = " (";
const char *comma = ", ";
const char *sep = paren;
if (full)
{
appendPQExpBuffer(&sql, "%sFULL%s", sep,
inplace ? " INPLACE" : "");
sep = comma;
}
if (freeze)
{
appendPQExpBuffer(&sql, "%sFREEZE", sep);
sep = comma;
}
if (verbose)
{
appendPQExpBuffer(&sql, "%sVERBOSE", sep);
sep = comma;
}
if (and_analyze)
{
appendPQExpBuffer(&sql, "%sANALYZE", sep);
sep = comma;
}
if (sep != paren)
appendPQExpBuffer(&sql, ")");
}
else
{
/*
* On older servers, VACUUM FULL is equivalent to VACUUM (FULL
* INPLACE) on newer servers, so we can ignore 'inplace'.
*/
if (full)
appendPQExpBuffer(&sql, " FULL");
if (freeze)
appendPQExpBuffer(&sql, " FREEZE");
if (verbose)
appendPQExpBuffer(&sql, " VERBOSE");
if (and_analyze)
appendPQExpBuffer(&sql, " ANALYZE");
}
if (verbose)
appendPQExpBuffer(&sql, " VERBOSE");
}
if (table)
appendPQExpBuffer(&sql, " %s", table);
appendPQExpBuffer(&sql, ";\n");
conn = connectDatabase(dbname, host, port, username, prompt_password, progname);
if (!executeMaintenanceCommand(conn, sql.data, echo))
{
if (table)
......@@ -252,7 +306,7 @@ vacuum_one_database(const char *dbname, bool full, bool verbose, bool and_analyz
static void
vacuum_all_databases(bool full, bool verbose, bool and_analyze, bool only_analyze,
vacuum_all_databases(bool full, bool inplace, bool verbose, bool and_analyze, bool only_analyze,
bool freeze, const char *host, const char *port,
const char *username, enum trivalue prompt_password,
const char *progname, bool echo, bool quiet)
......@@ -275,7 +329,7 @@ vacuum_all_databases(bool full, bool verbose, bool and_analyze, bool only_analyz
fflush(stdout);
}
vacuum_one_database(dbname, full, verbose, and_analyze, only_analyze,
vacuum_one_database(dbname, full, inplace, verbose, and_analyze, only_analyze,
freeze, NULL, host, port, username, prompt_password,
progname, echo);
}
......@@ -296,6 +350,7 @@ help(const char *progname)
printf(_(" -e, --echo show the commands being sent to the server\n"));
printf(_(" -f, --full do full vacuuming\n"));
printf(_(" -F, --freeze freeze row transaction information\n"));
printf(_(" -i, --inplace do full inplace vacuuming\n"));
printf(_(" -o, --only-analyze only update optimizer hints\n"));
printf(_(" -q, --quiet don't write any messages\n"));
printf(_(" -t, --table='TABLE[(COLUMNS)]' vacuum specific table only\n"));
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.37 2010/01/02 16:58:03 momjian Exp $
* $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.38 2010/01/06 05:31:14 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -18,7 +18,8 @@
extern void cluster(ClusterStmt *stmt, bool isTopLevel);
extern void cluster_rel(Oid tableOid, Oid indexOid, bool recheck,
bool verbose, int freeze_min_age, int freeze_table_age);
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck);
extern void mark_index_clustered(Relation rel, Oid indexOid);
......
......@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.422 2010/01/05 21:53:59 rhaas Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.423 2010/01/06 05:31:14 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -2244,7 +2244,8 @@ typedef enum VacuumOption
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
VACOPT_VERBOSE = 1 << 2, /* print progress info */
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
VACOPT_FULL = 1 << 4 /* FULL (non-concurrent) vacuum */
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
VACOPT_INPLACE = 1 << 5 /* traditional FULL INPLACE vacuum */
} VacuumOption;
typedef struct VacuumStmt
......
......@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.9 2010/01/02 16:58:07 momjian Exp $
* $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.10 2010/01/06 05:31:14 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -193,6 +193,7 @@ PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD)
PG_KEYWORD("inline", INLINE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("inner", INNER_P, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("inout", INOUT, COL_NAME_KEYWORD)
PG_KEYWORD("inplace", INPLACE, UNRESERVED_KEYWORD)
PG_KEYWORD("input", INPUT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("insensitive", INSENSITIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("insert", INSERT, UNRESERVED_KEYWORD)
......
......@@ -57,4 +57,65 @@ SELECT * FROM vactst;
(0 rows)
VACUUM (FULL, FREEZE) vactst;
VACUUM (ANALYZE, FULL INPLACE) vactst;
CREATE TABLE vaccluster (i INT PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "vaccluster_pkey" for table "vaccluster"
ALTER TABLE vaccluster CLUSTER ON vaccluster_pkey;
INSERT INTO vaccluster SELECT * FROM vactst;
CREATE TEMP TABLE vacid (
relid regclass,
filenode_0 oid,
filenode_1 oid,
filenode_2 oid,
filenode_3 oid
);
INSERT INTO vacid (relid, filenode_0)
SELECT oid, relfilenode FROM pg_class WHERE oid::regclass IN (
'pg_am', -- normal catalog
'pg_class', -- fundamental catalog
'pg_database', -- shared catalog
'vaccluster' , -- clustered table
'vacid', -- temp table
'vactst' -- normal table
);
-- only clusterd table should be changed
CLUSTER vaccluster;
UPDATE vacid SET filenode_1 = relfilenode
FROM pg_class WHERE oid = relid;
-- all tables should not be changed
VACUUM (FULL INPLACE) pg_am;
VACUUM (FULL INPLACE) pg_class;
VACUUM (FULL INPLACE) pg_database;
VACUUM (FULL INPLACE) vaccluster;
VACUUM (FULL INPLACE) vacid;
VACUUM (FULL INPLACE) vactst;
UPDATE vacid SET filenode_2 = relfilenode
FROM pg_class WHERE oid = relid;
-- only non-system tables should be changed
VACUUM FULL pg_am;
VACUUM FULL pg_class;
VACUUM FULL pg_database;
VACUUM FULL vaccluster;
VACUUM FULL vacid;
VACUUM FULL vactst;
UPDATE vacid SET filenode_3 = relfilenode
FROM pg_class WHERE oid = relid;
SELECT relid,
filenode_0 = filenode_1 AS cluster,
filenode_1 = filenode_2 AS full_inplace,
filenode_2 = filenode_3 AS full
FROM vacid
ORDER BY relid::text;
relid | cluster | full_inplace | full
-------------+---------+--------------+------
pg_am | t | t | t
pg_class | t | t | t
pg_database | t | t | t
vaccluster | f | t | f
vacid | t | t | f
vactst | t | t | f
(6 rows)
DROP TABLE vaccluster;
DROP TABLE vacid;
DROP TABLE vactst;
......@@ -40,5 +40,62 @@ DELETE FROM vactst;
SELECT * FROM vactst;
VACUUM (FULL, FREEZE) vactst;
VACUUM (ANALYZE, FULL INPLACE) vactst;
CREATE TABLE vaccluster (i INT PRIMARY KEY);
ALTER TABLE vaccluster CLUSTER ON vaccluster_pkey;
INSERT INTO vaccluster SELECT * FROM vactst;
CREATE TEMP TABLE vacid (
relid regclass,
filenode_0 oid,
filenode_1 oid,
filenode_2 oid,
filenode_3 oid
);
INSERT INTO vacid (relid, filenode_0)
SELECT oid, relfilenode FROM pg_class WHERE oid::regclass IN (
'pg_am', -- normal catalog
'pg_class', -- fundamental catalog
'pg_database', -- shared catalog
'vaccluster' , -- clustered table
'vacid', -- temp table
'vactst' -- normal table
);
-- only clusterd table should be changed
CLUSTER vaccluster;
UPDATE vacid SET filenode_1 = relfilenode
FROM pg_class WHERE oid = relid;
-- all tables should not be changed
VACUUM (FULL INPLACE) pg_am;
VACUUM (FULL INPLACE) pg_class;
VACUUM (FULL INPLACE) pg_database;
VACUUM (FULL INPLACE) vaccluster;
VACUUM (FULL INPLACE) vacid;
VACUUM (FULL INPLACE) vactst;
UPDATE vacid SET filenode_2 = relfilenode
FROM pg_class WHERE oid = relid;
-- only non-system tables should be changed
VACUUM FULL pg_am;
VACUUM FULL pg_class;
VACUUM FULL pg_database;
VACUUM FULL vaccluster;
VACUUM FULL vacid;
VACUUM FULL vactst;
UPDATE vacid SET filenode_3 = relfilenode
FROM pg_class WHERE oid = relid;
SELECT relid,
filenode_0 = filenode_1 AS cluster,
filenode_1 = filenode_2 AS full_inplace,
filenode_2 = filenode_3 AS full
FROM vacid
ORDER BY relid::text;
DROP TABLE vaccluster;
DROP TABLE vacid;
DROP TABLE vactst;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册