<!--
-$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
-->
<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>
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
-->
<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>
<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>
</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>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
} 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);
Oid tableOid,
indexOid = InvalidOid;
Relation rel;
- RelToCluster rvtc;
/* Find and lock the table */
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
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
{
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();
}
* 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;
* 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)
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;
return;
}
- /*
- * Check that the index still exists
- */
- if (!SearchSysCacheExists(RELOID,
- ObjectIdGetDatum(rvtc->indexOid),
- 0, 0, 0))
+ if (OidIsValid(indexOid))
{
- relation_close(OldHeap, AccessExclusiveLock);
- return;
- }
+ /*
+ * Check that the index still exists
+ */
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(indexOid),
+ 0, 0, 0))
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return;
+ }
- /*
- * Check that the index is still the one with indisclustered set.
- */
- tuple = SearchSysCache(INDEXRELID,
- ObjectIdGetDatum(rvtc->indexOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple)) /* probably can't happen */
- {
- relation_close(OldHeap, AccessExclusiveLock);
- return;
- }
- indexForm = (Form_pg_index) GETSTRUCT(tuple);
- if (!indexForm->indisclustered)
- {
+ /*
+ * Check that the index is still the one with indisclustered set.
+ */
+ tuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple)) /* probably can't happen */
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return;
+ }
+ indexForm = (Form_pg_index) GETSTRUCT(tuple);
+ if (!indexForm->indisclustered)
+ {
+ ReleaseSysCache(tuple);
+ relation_close(OldHeap, AccessExclusiveLock);
+ return;
+ }
ReleaseSysCache(tuple);
- relation_close(OldHeap, AccessExclusiveLock);
- return;
}
- 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 */
- ereport(verbose ? INFO : DEBUG2,
- (errmsg("clustering \"%s.%s\"",
- get_namespace_name(RelationGetNamespace(OldHeap)),
- RelationGetRelationName(OldHeap))));
- rebuild_relation(OldHeap, rvtc->indexOid);
+ if (OidIsValid(indexOid))
+ ereport(verbose ? INFO : DEBUG2,
+ (errmsg("clustering \"%s.%s\"",
+ get_namespace_name(RelationGetNamespace(OldHeap)),
+ RelationGetRelationName(OldHeap))));
+ 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
{
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);
/*
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);
}
}
/*
- * 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;
Relation newrel;
/* Mark the correct index as clustered */
- mark_index_clustered(OldHeap, indexOid);
+ if (OidIsValid(indexOid))
+ mark_index_clustered(OldHeap, indexOid);
/* Close relcache entry, but keep lock until transaction commit */
heap_close(OldHeap, NoLock);
/*
* 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();
* 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,
int natts;
Datum *values;
bool *isnull;
- IndexScanDesc scan;
- HeapTuple tuple;
+ IndexScanDesc indexScan;
+ HeapScanDesc heapScan;
bool use_wal;
TransactionId OldestXmin;
TransactionId FreezeXid;
*/
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
- OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
+ if (OidIsValid(OIDOldIndex))
+ OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
+ else
+ OldIndex = NULL;
/*
* Their tuple descriptors should be exactly alike, but here we only need
* 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
* 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();
- /* Since we used no scan keys, should never need to recheck */
- if (scan->xs_recheck)
- elog(ERROR, "CLUSTER does not support lossy index conditions");
+ if (OldIndex != NULL)
+ {
+ tuple = index_getnext(indexScan, ForwardScanDirection);
+ if (tuple == NULL)
+ break;
- LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
+ /* Since we used no scan keys, should never need to recheck */
+ if (indexScan->xs_recheck)
+ elog(ERROR, "CLUSTER does not support lossy index conditions");
- switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
- scan->xs_cbuf))
+ buf = indexScan->xs_cbuf;
+ }
+ else
+ {
+ tuple = heap_getnext(heapScan, ForwardScanDirection);
+ if (tuple == NULL)
+ break;
+
+ buf = heapScan->rs_cbuf;
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_SHARE);
+
+ switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
break;
}
- LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (isdead)
{
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);
pfree(values);
pfree(isnull);
- index_close(OldIndex, NoLock);
+ if (OldIndex != NULL)
+ index_close(OldIndex, NoLock);
heap_close(OldHeap, NoLock);
heap_close(NewHeap, NoLock);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
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";
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);
SetUserIdAndSecContext(save_userid, save_sec_context);
/* all done with this class, but hold lock until commit */
- relation_close(onerel, NoLock);
+ if (onerel)
+ relation_close(onerel, NoLock);
/*
* Complete the transaction and free all temporary memory used.
*
*
* 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
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
| VERBOSE { $$ = VACOPT_VERBOSE; }
| FREEZE { $$ = VACOPT_FREEZE; }
| FULL { $$ = VACOPT_FULL; }
+ | FULL INPLACE { $$ = VACOPT_FULL | VACOPT_INPLACE; }
;
AnalyzeStmt:
| INHERIT
| INHERITS
| INLINE_P
+ | INPLACE
| INPUT_P
| INSENSITIVE
| INSERT
* 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 $
*
*-------------------------------------------------------------------------
*/
#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,
{"table", required_argument, NULL, 't'},
{"full", no_argument, NULL, 'f'},
{"verbose", no_argument, NULL, 'v'},
+ {"inplace", no_argument, NULL, 'i'},
{NULL, 0, NULL, 0}
};
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)
{
case 'f':
full = true;
break;
+ case 'i':
+ inplace = true;
+ break;
case 'v':
verbose = true;
break;
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)
{
/* ignore 'and_analyze' */
}
+ setup_cancel_handler();
+
if (alldb)
{
if (dbname)
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);
}
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);
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,
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 (full)
- appendPQExpBuffer(&sql, " FULL");
- if (freeze)
- appendPQExpBuffer(&sql, " FREEZE");
- if (and_analyze)
- appendPQExpBuffer(&sql, " ANALYZE");
+ 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)
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)
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);
}
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"));
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
(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;
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;