diff -cprN head/doc/src/sgml/ref/vacuum.sgml work/doc/src/sgml/ref/vacuum.sgml *** head/doc/src/sgml/ref/vacuum.sgml 2009-11-16 17:07:47.970422831 +0900 --- work/doc/src/sgml/ref/vacuum.sgml 2009-11-16 18:00:56.724438000 +0900 *************** PostgreSQL documentation *** 21,29 **** ! VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ table [ (column [, ...] ) ] ] ! VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table ] ! VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] --- 21,29 ---- ! VACUUM [ ( { FULL [ INPLACE | REPLACE ] | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ table [ (column [, ...] ) ] ] ! VACUUM [ FULL [ INPLACE | REPLACE ] ] [ FREEZE ] [ VERBOSE ] [ table ] ! VACUUM [ FULL [ INPLACE | REPLACE ] ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] *************** VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] A *** 76,81 **** --- 76,92 ---- Selects full vacuum, which can reclaim more space, but takes much longer and exclusively locks the table. + INPLACE option consumes less disk space but takes + longer time, and REPLACE option vice versa. + The default is REPLACE. + + + VACUUM (FULL INPLACE) behaves as a traditional + VACUUM FULL command. It moves tuples in the table + from the tail to the head of files. On the other hand, + VACUUM (FULL REPLACE) behaves as a + CLUSTER command without any sorting. + It moves all tuples into a new table and swap the old and the new tables. diff -cprN head/doc/src/sgml/ref/vacuumdb.sgml work/doc/src/sgml/ref/vacuumdb.sgml *** head/doc/src/sgml/ref/vacuumdb.sgml 2009-02-27 01:02:37.000000000 +0900 --- work/doc/src/sgml/ref/vacuumdb.sgml 2009-11-16 18:02:28.892988000 +0900 *************** PostgreSQL documentation *** 24,29 **** --- 24,30 ---- vacuumdb connection-option --full-f + --inplace-i --verbose-v --analyze-z --freeze-F *************** PostgreSQL documentation *** 36,41 **** --- 37,43 ---- connection-options --all-a --full-f + --inplace-i --verbose-v --analyze-z --freeze-F *************** PostgreSQL documentation *** 111,117 **** ! Perform full vacuuming. --- 113,129 ---- ! Perform full replace vacuuming. ! ! ! ! ! ! ! ! ! ! Perform full inplace vacuuming. diff -cprN head/src/backend/commands/cluster.c work/src/backend/commands/cluster.c *** head/src/backend/commands/cluster.c 2009-10-06 04:24:36.000000000 +0900 --- work/src/backend/commands/cluster.c 2009-11-16 17:09:38.253673220 +0900 *************** typedef struct *** 61,69 **** } 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 List *get_tables_to_cluster(MemoryContext cluster_context); --- 61,70 ---- } RelToCluster; ! 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); *************** cluster(ClusterStmt *stmt, bool isTopLev *** 101,107 **** Oid tableOid, indexOid = InvalidOid; Relation rel; - RelToCluster rvtc; /* Find and lock the table */ rel = heap_openrv(stmt->relation, AccessExclusiveLock); --- 102,107 ---- *************** cluster(ClusterStmt *stmt, bool isTopLev *** 169,183 **** 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); } else { --- 169,179 ---- stmt->indexname, stmt->relation->relname))); } /* close relation, keep lock till commit */ heap_close(rel, NoLock); /* Do the job */ ! cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1); } else { *************** cluster(ClusterStmt *stmt, bool isTopLev *** 226,232 **** StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); ! cluster_rel(rvtc, true, stmt->verbose); PopActiveSnapshot(); CommitTransactionCommand(); } --- 222,228 ---- StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); ! cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose, -1, -1); PopActiveSnapshot(); CommitTransactionCommand(); } *************** cluster(ClusterStmt *stmt, bool isTopLev *** 253,260 **** * the new table, it's better to create the indexes afterwards than to fill * them incrementally while we load the table. */ ! static void ! cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose) { Relation OldHeap; --- 249,257 ---- * the new table, it's better to create the indexes afterwards than to fill * them incrementally while we load the table. */ ! void ! cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, ! int freeze_min_age, int freeze_table_age) { Relation OldHeap; *************** cluster_rel(RelToCluster *rvtc, bool rec *** 267,273 **** * case, since cluster() already did it.) The index lock is taken inside * check_index_is_clusterable. */ ! OldHeap = try_relation_open(rvtc->tableOid, AccessExclusiveLock); /* If the table has gone away, we can skip processing it */ if (!OldHeap) --- 264,270 ---- * case, since cluster() already did it.) The index lock is taken inside * check_index_is_clusterable. */ ! OldHeap = try_relation_open(tableOid, AccessExclusiveLock); /* If the table has gone away, we can skip processing it */ if (!OldHeap) *************** cluster_rel(RelToCluster *rvtc, bool rec *** 287,293 **** Form_pg_index indexForm; /* Check that the user still owns the relation */ ! if (!pg_class_ownercheck(rvtc->tableOid, GetUserId())) { relation_close(OldHeap, AccessExclusiveLock); return; --- 284,290 ---- Form_pg_index indexForm; /* Check that the user still owns the relation */ ! if (!pg_class_ownercheck(tableOid, GetUserId())) { relation_close(OldHeap, AccessExclusiveLock); return; *************** cluster_rel(RelToCluster *rvtc, bool rec *** 308,360 **** return; } ! /* ! * Check that the index still exists ! */ ! if (!SearchSysCacheExists(RELOID, ! ObjectIdGetDatum(rvtc->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) ! { ReleaseSysCache(tuple); - relation_close(OldHeap, AccessExclusiveLock); - return; } - ReleaseSysCache(tuple); } ! /* Check index is valid to cluster on */ ! check_index_is_clusterable(OldHeap, rvtc->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); /* NB: rebuild_relation does heap_close() on OldHeap */ } /* ! * Verify that the specified index is a legitimate index 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 --- 305,360 ---- return; } ! if (OidIsValid(indexOid)) { ! /* ! * 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(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); } } ! /* 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, indexOid, freeze_min_age, freeze_table_age); /* NB: rebuild_relation does heap_close() on OldHeap */ } /* ! * 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 *************** check_index_is_clusterable(Relation OldH *** 366,371 **** --- 366,403 ---- { 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); /* *************** check_index_is_clusterable(Relation OldH *** 448,481 **** 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); } --- 480,485 ---- *************** mark_index_clustered(Relation rel, Oid i *** 565,571 **** * NB: this routine closes OldHeap at the right time; caller should not. */ static void ! rebuild_relation(Relation OldHeap, Oid indexOid) { Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; --- 569,576 ---- * NB: this routine closes OldHeap at the right time; caller should not. */ static void ! rebuild_relation(Relation OldHeap, Oid indexOid, ! int freeze_min_age, int freeze_table_age) { Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; *************** rebuild_relation(Relation OldHeap, Oid i *** 576,582 **** Relation newrel; /* Mark the correct index as clustered */ ! mark_index_clustered(OldHeap, indexOid); /* Close relcache entry, but keep lock until transaction commit */ heap_close(OldHeap, NoLock); --- 581,588 ---- 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 */ heap_close(OldHeap, NoLock); *************** rebuild_relation(Relation OldHeap, Oid i *** 599,605 **** /* * Copy the heap data into the new table in the desired order. */ ! frozenXid = copy_heap_data(OIDNewHeap, tableOid, indexOid); /* To make the new heap's data visible (probably not needed?). */ CommandCounterIncrement(); --- 605,612 ---- /* * Copy the heap data into the new table in the desired order. */ ! 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(); *************** make_new_heap(Oid OIDOldHeap, const char *** 758,764 **** * freeze cutoff point for the tuples. */ static TransactionId ! copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) { Relation NewHeap, OldHeap, --- 765,772 ---- * freeze cutoff point for the tuples. */ static TransactionId ! copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, ! int freeze_min_age, int freeze_table_age) { Relation NewHeap, OldHeap, *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 768,775 **** int natts; Datum *values; bool *isnull; ! IndexScanDesc scan; ! HeapTuple tuple; bool use_wal; TransactionId OldestXmin; TransactionId FreezeXid; --- 776,783 ---- int natts; Datum *values; bool *isnull; ! IndexScanDesc indexScan; ! HeapScanDesc heapScan; bool use_wal; TransactionId OldestXmin; TransactionId FreezeXid; *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 780,786 **** */ NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock); OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); ! OldIndex = index_open(OIDOldIndex, AccessExclusiveLock); /* * Their tuple descriptors should be exactly alike, but here we only need --- 788,797 ---- */ 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 *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 809,816 **** * 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); /* * FreezeXid will become the table's new relfrozenxid, and that mustn't go --- 820,827 ---- * freeze_min_age to avoid having CLUSTER freeze tuples earlier than a * plain VACUUM would. */ ! 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 *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 828,852 **** * copied, we scan with SnapshotAny and use HeapTupleSatisfiesVacuum for * the visibility test. */ ! scan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, (ScanKey) NULL); ! while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL) { HeapTuple copiedTuple; 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"); ! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE); ! switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin, ! scan->xs_cbuf)) { case HEAPTUPLE_DEAD: /* Definitely dead */ --- 839,884 ---- * copied, we scan with SnapshotAny and use HeapTupleSatisfiesVacuum for * the visibility test. */ ! if (OldIndex != NULL) ! indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, (ScanKey) NULL); + else + heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) 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 (indexScan->xs_recheck) ! elog(ERROR, "CLUSTER does not support lossy index conditions"); ! 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 */ *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 888,894 **** break; } ! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK); if (isdead) { --- 920,926 ---- break; } ! LockBuffer(buf, BUFFER_LOCK_UNLOCK); if (isdead) { *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 932,938 **** heap_freetuple(copiedTuple); } ! index_endscan(scan); /* Write out any remaining tuples, and fsync if needed */ end_heap_rewrite(rwstate); --- 964,973 ---- heap_freetuple(copiedTuple); } ! if (OldIndex != NULL) ! index_endscan(indexScan); ! else ! heap_endscan(heapScan); /* Write out any remaining tuples, and fsync if needed */ end_heap_rewrite(rwstate); *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 940,946 **** pfree(values); pfree(isnull); ! index_close(OldIndex, NoLock); heap_close(OldHeap, NoLock); heap_close(NewHeap, NoLock); --- 975,982 ---- pfree(values); pfree(isnull); ! if (OldIndex != NULL) ! index_close(OldIndex, NoLock); heap_close(OldHeap, NoLock); heap_close(NewHeap, NoLock); diff -cprN head/src/backend/commands/vacuum.c work/src/backend/commands/vacuum.c *** head/src/backend/commands/vacuum.c 2009-11-16 17:07:47.972646206 +0900 --- work/src/backend/commands/vacuum.c 2009-11-16 17:48:38.223397735 +0900 *************** *** 29,38 **** --- 29,40 ---- #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" *************** vacuum(VacuumStmt *vacstmt, Oid relid, b *** 300,305 **** --- 302,309 ---- Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE)); Assert(!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)) || (vacstmt->options & VACOPT_VACUUM)); + Assert(!(vacstmt->options & VACOPT_INPLACE) || + (vacstmt->options & VACOPT_FULL)); Assert(vacstmt->va_cols == NIL || (vacstmt->options & VACOPT_ANALYZE)); stmttype = (vacstmt->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE"; *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 1194,1211 **** SetUserIdAndContext(onerel->rd_rel->relowner, true); /* ! * Do the actual work --- either FULL or "lazy" vacuum */ ! if (vacstmt->options & VACOPT_FULL) ! heldoff = full_vacuum_rel(onerel, vacstmt); ! else heldoff = lazy_vacuum_rel(onerel, vacstmt, vac_strategy, scanned_all); ! ! /* Restore userid */ ! SetUserIdAndContext(save_userid, save_secdefcxt); ! ! /* all done with this class, but hold lock until commit */ ! relation_close(onerel, NoLock); /* * Complete the transaction and free all temporary memory used. --- 1198,1228 ---- SetUserIdAndContext(onerel->rd_rel->relowner, true); /* ! * Do the actual work --- either FULL REPLACE, INPLACE, or "lazy" vacuum. ! * We cannot use FULL REPLACE for system relations. */ ! 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 ! { ! /* 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; ! } ! ! /* Restore userid */ ! SetUserIdAndContext(save_userid, save_secdefcxt); ! ! /* all done with this class, but hold lock until commit */ ! if (onerel) ! relation_close(onerel, NoLock); /* * Complete the transaction and free all temporary memory used. diff -cprN head/src/backend/parser/gram.y work/src/backend/parser/gram.y *** head/src/backend/parser/gram.y 2009-11-16 17:07:47.978829617 +0900 --- work/src/backend/parser/gram.y 2009-11-16 17:48:47.343427157 +0900 *************** static TypeName *TableFuncTypeName(List *** 485,491 **** 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 INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION JOIN --- 485,491 ---- IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P ! INNER_P INOUT INPLACE INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION JOIN *************** vacuum_option_elem: *** 6701,6706 **** --- 6701,6708 ---- | VERBOSE { $$ = VACOPT_VERBOSE; } | FREEZE { $$ = VACOPT_FREEZE; } | FULL { $$ = VACOPT_FULL; } + | FULL REPLACE { $$ = VACOPT_FULL; } + | FULL INPLACE { $$ = VACOPT_FULL | VACOPT_INPLACE; } ; AnalyzeStmt: *************** unreserved_keyword: *** 10642,10647 **** --- 10644,10650 ---- | INHERIT | INHERITS | INLINE_P + | INPLACE | INPUT_P | INSENSITIVE | INSERT diff -cprN head/src/bin/scripts/vacuumdb.c work/src/bin/scripts/vacuumdb.c *** head/src/bin/scripts/vacuumdb.c 2009-10-16 19:38:25.000000000 +0900 --- work/src/bin/scripts/vacuumdb.c 2009-11-16 18:26:07.662610000 +0900 *************** *** 14,28 **** #include "common.h" ! static void vacuum_one_database(const char *dbname, bool full, bool verbose, bool 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 analyze, bool freeze, ! const char *host, const char *port, ! const char *username, enum trivalue prompt_password, ! const char *progname, bool echo, bool quiet); static void help(const char *progname); --- 14,29 ---- #include "common.h" ! static void vacuum_one_database(const char *dbname, bool full, bool inplace, ! bool verbose, bool 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 inplace, ! bool verbose, bool analyze, bool freeze, ! const char *host, const char *port, ! const char *username, enum trivalue prompt_password, ! const char *progname, bool echo, bool quiet); static void help(const char *progname); *************** main(int argc, char *argv[]) *** 45,50 **** --- 46,52 ---- {"table", required_argument, NULL, 't'}, {"full", no_argument, NULL, 'f'}, {"verbose", no_argument, NULL, 'v'}, + {"inplace", no_argument, NULL, 'i'}, {NULL, 0, NULL, 0} }; *************** main(int argc, char *argv[]) *** 65,77 **** char *table = NULL; bool full = false; bool verbose = 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) { switch (c) { --- 67,80 ---- 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:fiv", long_options, &optindex)) != -1) { switch (c) { *************** main(int argc, char *argv[]) *** 117,122 **** --- 120,128 ---- case 'v': verbose = true; break; + case 'i': + inplace = true; + break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); *************** main(int argc, char *argv[]) *** 154,160 **** exit(1); } ! vacuum_all_databases(full, verbose, analyze, freeze, host, port, username, prompt_password, progname, echo, quiet); } --- 160,166 ---- exit(1); } ! vacuum_all_databases(full, inplace, verbose, analyze, freeze, host, port, username, prompt_password, progname, echo, quiet); } *************** main(int argc, char *argv[]) *** 170,177 **** dbname = get_user_name(progname); } ! vacuum_one_database(dbname, full, verbose, analyze, freeze, table, ! host, port, username, prompt_password, progname, echo); } --- 176,183 ---- dbname = get_user_name(progname); } ! vacuum_one_database(dbname, full, inplace, verbose, analyze, freeze, ! table, host, port, username, prompt_password, progname, echo); } *************** main(int argc, char *argv[]) *** 180,187 **** static void ! vacuum_one_database(const char *dbname, bool full, bool verbose, bool analyze, ! bool freeze, const char *table, const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo) --- 186,193 ---- static void ! vacuum_one_database(const char *dbname, bool full, bool inplace, bool verbose, ! bool analyze, bool freeze, const char *table, const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo) *************** vacuum_one_database(const char *dbname, *** 193,206 **** initPQExpBuffer(&sql); appendPQExpBuffer(&sql, "VACUUM"); ! if (full) ! appendPQExpBuffer(&sql, " FULL"); ! if (freeze) ! appendPQExpBuffer(&sql, " FREEZE"); ! if (verbose) ! appendPQExpBuffer(&sql, " VERBOSE"); ! if (analyze) ! appendPQExpBuffer(&sql, " ANALYZE"); if (table) appendPQExpBuffer(&sql, " %s", table); appendPQExpBuffer(&sql, ";\n"); --- 199,226 ---- initPQExpBuffer(&sql); appendPQExpBuffer(&sql, "VACUUM"); ! if (inplace) ! { ! appendPQExpBuffer(&sql, " (FULL INPLACE"); ! if (freeze) ! appendPQExpBuffer(&sql, ", FREEZE"); ! if (verbose) ! appendPQExpBuffer(&sql, ", VERBOSE"); ! if (analyze) ! appendPQExpBuffer(&sql, ", ANALYZE"); ! appendPQExpBuffer(&sql, ")"); ! } ! else ! { ! if (full) ! appendPQExpBuffer(&sql, " FULL"); ! if (freeze) ! appendPQExpBuffer(&sql, " FREEZE"); ! if (verbose) ! appendPQExpBuffer(&sql, " VERBOSE"); ! if (analyze) ! appendPQExpBuffer(&sql, " ANALYZE"); ! } if (table) appendPQExpBuffer(&sql, " %s", table); appendPQExpBuffer(&sql, ";\n"); *************** vacuum_one_database(const char *dbname, *** 223,230 **** static void ! vacuum_all_databases(bool full, bool verbose, bool analyze, bool freeze, ! const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo, bool quiet) { --- 243,250 ---- static void ! vacuum_all_databases(bool full, bool inplace, bool verbose, bool analyze, ! bool freeze, const char *host, const char *port, const char *username, enum trivalue prompt_password, const char *progname, bool echo, bool quiet) { *************** vacuum_all_databases(bool full, bool ver *** 246,253 **** fflush(stdout); } ! vacuum_one_database(dbname, full, verbose, analyze, freeze, NULL, ! host, port, username, prompt_password, progname, echo); } --- 266,273 ---- fflush(stdout); } ! vacuum_one_database(dbname, full, inplace, verbose, analyze, freeze, ! NULL, host, port, username, prompt_password, progname, echo); } *************** help(const char *progname) *** 267,272 **** --- 287,293 ---- 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(_(" -q, --quiet don't write any messages\n")); printf(_(" -t, --table='TABLE[(COLUMNS)]' vacuum specific table only\n")); printf(_(" -v, --verbose write a lot of output\n")); diff -cprN head/src/include/commands/cluster.h work/src/include/commands/cluster.h *** head/src/include/commands/cluster.h 2009-01-02 02:23:58.000000000 +0900 --- work/src/include/commands/cluster.h 2009-11-16 17:09:38.255673168 +0900 *************** *** 18,24 **** extern void cluster(ClusterStmt *stmt, bool isTopLevel); ! extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck); extern void mark_index_clustered(Relation rel, Oid indexOid); --- 18,25 ---- 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); diff -cprN head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h *** head/src/include/nodes/parsenodes.h 2009-11-16 17:07:47.982429600 +0900 --- work/src/include/nodes/parsenodes.h 2009-11-16 17:18:36.681387181 +0900 *************** typedef enum VacuumOption *** 2219,2225 **** VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */ VACOPT_VERBOSE = 1 << 2, /* VERBOSE option */ VACOPT_FREEZE = 1 << 3, /* FREEZE option */ ! VACOPT_FULL = 1 << 4 /* FULL option */ } VacuumOption; typedef struct VacuumStmt --- 2219,2226 ---- VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */ VACOPT_VERBOSE = 1 << 2, /* VERBOSE option */ VACOPT_FREEZE = 1 << 3, /* FREEZE option */ ! VACOPT_FULL = 1 << 4, /* FULL option */ ! VACOPT_INPLACE = 1 << 5, /* use traditional VACUUM FULL */ } VacuumOption; typedef struct VacuumStmt diff -cprN head/src/include/parser/kwlist.h work/src/include/parser/kwlist.h *** head/src/include/parser/kwlist.h 2009-11-06 08:24:27.000000000 +0900 --- work/src/include/parser/kwlist.h 2009-11-16 17:09:38.254673056 +0900 *************** PG_KEYWORD("initially", INITIALLY, RESER *** 192,197 **** --- 192,198 ---- 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)