vacuum-full_20091027.patch

application/octet-stream

Filename: vacuum-full_20091027.patch
Type: application/octet-stream
Part: 0
Message: New VACUUM FULL

Patch

Format: context
File+
src/backend/commands/analyze.c 5 5
src/backend/commands/cluster.c 128 92
src/backend/commands/vacuum.c 69 25
src/backend/commands/vacuumlazy.c 3 2
src/backend/nodes/copyfuncs.c 1 4
src/backend/nodes/equalfuncs.c 1 4
src/backend/parser/gram.y 54 20
src/backend/postmaster/autovacuum.c 4 4
src/backend/tcop/utility.c 1 1
src/include/commands/cluster.h 2 1
src/include/nodes/parsenodes.h 14 4
src/include/parser/kwlist.h 1 0
diff -cpr head/src/backend/commands/analyze.c work/src/backend/commands/analyze.c
*** head/src/backend/commands/analyze.c	2009-08-13 03:23:49.000000000 +0900
--- work/src/backend/commands/analyze.c	2009-10-27 10:15:06.817275251 +0900
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 135,141 ****
  	Oid			save_userid;
  	bool		save_secdefcxt;
  
! 	if (vacstmt->verbose)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
--- 135,141 ----
  	Oid			save_userid;
  	bool		save_secdefcxt;
  
! 	if (vacstmt->options & VACOPT_VERBOSE)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 173,179 ****
  		  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))
  	{
  		/* No need for a WARNING if we already complained during VACUUM */
! 		if (!vacstmt->vacuum)
  		{
  			if (onerel->rd_rel->relisshared)
  				ereport(WARNING,
--- 173,179 ----
  		  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))
  	{
  		/* No need for a WARNING if we already complained during VACUUM */
! 		if (!(vacstmt->options & VACOPT_VACUUM))
  		{
  			if (onerel->rd_rel->relisshared)
  				ereport(WARNING,
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 199,205 ****
  	if (onerel->rd_rel->relkind != RELKIND_RELATION)
  	{
  		/* No need for a WARNING if we already complained during VACUUM */
! 		if (!vacstmt->vacuum)
  			ereport(WARNING,
  					(errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables",
  							RelationGetRelationName(onerel))));
--- 199,205 ----
  	if (onerel->rd_rel->relkind != RELKIND_RELATION)
  	{
  		/* No need for a WARNING if we already complained during VACUUM */
! 		if (!(vacstmt->options & VACOPT_VACUUM))
  			ereport(WARNING,
  					(errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables",
  							RelationGetRelationName(onerel))));
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 475,481 ****
  	 * VACUUM ANALYZE, don't overwrite the accurate count already inserted by
  	 * VACUUM.
  	 */
! 	if (!vacstmt->vacuum)
  	{
  		for (ind = 0; ind < nindexes; ind++)
  		{
--- 475,481 ----
  	 * VACUUM ANALYZE, don't overwrite the accurate count already inserted by
  	 * VACUUM.
  	 */
! 	if (!(vacstmt->options & VACOPT_VACUUM))
  	{
  		for (ind = 0; ind < nindexes; ind++)
  		{
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 493,499 ****
  cleanup:
  
  	/* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */
! 	if (!vacstmt->vacuum)
  	{
  		for (ind = 0; ind < nindexes; ind++)
  		{
--- 493,499 ----
  cleanup:
  
  	/* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */
! 	if (!(vacstmt->options & VACOPT_VACUUM))
  	{
  		for (ind = 0; ind < nindexes; ind++)
  		{
diff -cpr 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-10-27 12:58:36.218261609 +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 -cpr head/src/backend/commands/vacuum.c work/src/backend/commands/vacuum.c
*** head/src/backend/commands/vacuum.c	2009-10-26 11:26:29.000000000 +0900
--- work/src/backend/commands/vacuum.c	2009-10-27 13:24:51.283271344 +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"
*************** void
*** 289,302 ****
  vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  	   BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
  {
! 	const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
  	volatile MemoryContext anl_context = NULL;
  	volatile bool all_rels,
  				in_outer_xact,
  				use_own_xacts;
  	List	   *relations;
  
! 	if (vacstmt->verbose)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
--- 291,319 ----
  vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  	   BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
  {
! 	const char *stmttype;
  	volatile MemoryContext anl_context = NULL;
  	volatile bool all_rels,
  				in_outer_xact,
  				use_own_xacts;
  	List	   *relations;
  
! 	/* Adjust options. */
! 	if (vacstmt->options & (VACOPT_INPLACE | VACOPT_REPLACE | VACOPT_FREEZE))
! 		vacstmt->options |= VACOPT_VACUUM;
! 	if (vacstmt->va_cols)
! 		vacstmt->options |= VACOPT_ANALYZE;
! 
! 	Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
! 	stmttype = (vacstmt->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
! 
! 	if (vacstmt->options & VACOPT_FREEZE)
! 	{
! 		vacstmt->freeze_min_age = 0;
! 		vacstmt->freeze_table_age = 0;
! 	}
! 
! 	if (vacstmt->options & VACOPT_VERBOSE)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 315,336 ****
  	 *
  	 * ANALYZE (without VACUUM) can run either way.
  	 */
! 	if (vacstmt->vacuum)
  	{
  		PreventTransactionChain(isTopLevel, stmttype);
  		in_outer_xact = false;
  	}
  	else
  		in_outer_xact = IsInTransactionChain(isTopLevel);
  
  	/*
- 	 * Send info about dead objects to the statistics collector, unless we are
- 	 * in autovacuum --- autovacuum.c does this for itself.
- 	 */
- 	if (vacstmt->vacuum && !IsAutoVacuumWorkerProcess())
- 		pgstat_vacuum_stat();
- 
- 	/*
  	 * Create special memory context for cross-transaction storage.
  	 *
  	 * Since it is a child of PortalContext, it will go away eventually even
--- 332,353 ----
  	 *
  	 * ANALYZE (without VACUUM) can run either way.
  	 */
! 	if (vacstmt->options & VACOPT_VACUUM)
  	{
  		PreventTransactionChain(isTopLevel, stmttype);
  		in_outer_xact = false;
+ 
+ 		/*
+ 		 * Send info about dead objects to the statistics collector, unless we are
+ 		 * in autovacuum --- autovacuum.c does this for itself.
+ 		 */
+ 		if (!IsAutoVacuumWorkerProcess())
+ 			pgstat_vacuum_stat();
  	}
  	else
  		in_outer_xact = IsInTransactionChain(isTopLevel);
  
  	/*
  	 * Create special memory context for cross-transaction storage.
  	 *
  	 * Since it is a child of PortalContext, it will go away eventually even
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 378,388 ****
  	 * transaction block, and also in an autovacuum worker, use own
  	 * transactions so we can release locks sooner.
  	 */
! 	if (vacstmt->vacuum)
  		use_own_xacts = true;
  	else
  	{
! 		Assert(vacstmt->analyze);
  		if (IsAutoVacuumWorkerProcess())
  			use_own_xacts = true;
  		else if (in_outer_xact)
--- 395,405 ----
  	 * transaction block, and also in an autovacuum worker, use own
  	 * transactions so we can release locks sooner.
  	 */
! 	if (vacstmt->options & VACOPT_VACUUM)
  		use_own_xacts = true;
  	else
  	{
! 		Assert(vacstmt->options & VACOPT_ANALYZE);
  		if (IsAutoVacuumWorkerProcess())
  			use_own_xacts = true;
  		else if (in_outer_xact)
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 438,448 ****
  			Oid			relid = lfirst_oid(cur);
  			bool		scanned_all = false;
  
! 			if (vacstmt->vacuum)
! 				vacuum_rel(relid, vacstmt, do_toast, for_wraparound,
! 						   &scanned_all);
  
! 			if (vacstmt->analyze)
  			{
  				MemoryContext old_context = NULL;
  
--- 455,465 ----
  			Oid			relid = lfirst_oid(cur);
  			bool		scanned_all = false;
  
! 			if (vacstmt->options & VACOPT_VACUUM)
! 				vacuum_rel(relid, vacstmt, do_toast, for_wraparound,
! 						   &scanned_all);
  
! 			if (vacstmt->options & VACOPT_ANALYZE)
  			{
  				MemoryContext old_context = NULL;
  
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 502,508 ****
  		StartTransactionCommand();
  	}
  
! 	if (vacstmt->vacuum && !IsAutoVacuumWorkerProcess())
  	{
  		/*
  		 * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
--- 519,525 ----
  		StartTransactionCommand();
  	}
  
! 	if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
  	{
  		/*
  		 * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1033,1039 ****
  	 */
  	PushActiveSnapshot(GetTransactionSnapshot());
  
! 	if (!vacstmt->full)
  	{
  		/*
  		 * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
--- 1050,1056 ----
  	 */
  	PushActiveSnapshot(GetTransactionSnapshot());
  
! 	if (!(vacstmt->options & (VACOPT_INPLACE | VACOPT_REPLACE)))
  	{
  		/*
  		 * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1073,1079 ****
  	 * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
  	 * way, we can be sure that no other backend is vacuuming the same table.
  	 */
! 	lmode = vacstmt->full ? AccessExclusiveLock : ShareUpdateExclusiveLock;
  
  	/*
  	 * Open the relation and get the appropriate lock on it.
--- 1090,1098 ----
  	 * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
  	 * way, we can be sure that no other backend is vacuuming the same table.
  	 */
! 	lmode = (vacstmt->options & VACOPT_INPLACE)
! 				? AccessExclusiveLock
! 				: ShareUpdateExclusiveLock;
  
  	/*
  	 * Open the relation and get the appropriate lock on it.
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1183,1200 ****
  	SetUserIdAndContext(onerel->rd_rel->relowner, true);
  
  	/*
! 	 * Do the actual work --- either FULL or "lazy" vacuum
  	 */
! 	if (vacstmt->full)
! 		full_vacuum_rel(onerel, vacstmt);
  	else
! 		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.
--- 1202,1244 ----
  	SetUserIdAndContext(onerel->rd_rel->relowner, true);
  
  	/*
! 	 * Do the actual work --- either FULL REPLACE, INPLACE, or "lazy" vacuum
  	 */
! 	if (vacstmt->options & VACOPT_REPLACE)
! 	{
! 		if (IsSystemRelation(onerel))
! 		{
! 			/*
! 			 * Since we cannot FULL REPLACE vacuum for system relations,
! 			 * do FULL INPLACE vacuum instead.
! 			 */
! 			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);
! 		}
! 	}
  	else
! 	{
! 		if (vacstmt->options & VACOPT_INPLACE)
! 			full_vacuum_rel(onerel, vacstmt);
! 		else
! 			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 */
! 	if (onerel)
! 		relation_close(onerel, NoLock);
  
  	/*
  	 * Complete the transaction and free all temporary memory used.
*************** full_vacuum_rel(Relation onerel, VacuumS
*** 1323,1329 ****
  
  	/* report results to the stats collector, too */
  	pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
! 						 true, vacstmt->analyze, vacrelstats->rel_tuples);
  }
  
  
--- 1367,1373 ----
  
  	/* report results to the stats collector, too */
  	pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
! 		 true, (vacstmt->options & VACOPT_ANALYZE) != 0, vacrelstats->rel_tuples);
  }
  
  
diff -cpr head/src/backend/commands/vacuumlazy.c work/src/backend/commands/vacuumlazy.c
*** head/src/backend/commands/vacuumlazy.c	2009-08-24 11:18:32.000000000 +0900
--- work/src/backend/commands/vacuumlazy.c	2009-10-27 10:15:06.820375310 +0900
*************** lazy_vacuum_rel(Relation onerel, VacuumS
*** 160,166 ****
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration > 0)
  		starttime = GetCurrentTimestamp();
  
! 	if (vacstmt->verbose)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
--- 160,166 ----
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration > 0)
  		starttime = GetCurrentTimestamp();
  
! 	if (vacstmt->options & VACOPT_VERBOSE)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
*************** lazy_vacuum_rel(Relation onerel, VacuumS
*** 222,228 ****
  	pgstat_report_vacuum(RelationGetRelid(onerel),
  						 onerel->rd_rel->relisshared,
  						 vacrelstats->scanned_all,
! 						 vacstmt->analyze, vacrelstats->rel_tuples);
  
  	/* and log the action if appropriate */
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
--- 222,229 ----
  	pgstat_report_vacuum(RelationGetRelid(onerel),
  						 onerel->rd_rel->relisshared,
  						 vacrelstats->scanned_all,
! 						 (vacstmt->options & VACOPT_ANALYZE) != 0,
! 						 vacrelstats->rel_tuples);
  
  	/* and log the action if appropriate */
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
diff -cpr head/src/backend/nodes/copyfuncs.c work/src/backend/nodes/copyfuncs.c
*** head/src/backend/nodes/copyfuncs.c	2009-10-26 11:26:31.000000000 +0900
--- work/src/backend/nodes/copyfuncs.c	2009-10-27 10:15:06.821324102 +0900
*************** _copyVacuumStmt(VacuumStmt *from)
*** 2955,2964 ****
  {
  	VacuumStmt *newnode = makeNode(VacuumStmt);
  
! 	COPY_SCALAR_FIELD(vacuum);
! 	COPY_SCALAR_FIELD(full);
! 	COPY_SCALAR_FIELD(analyze);
! 	COPY_SCALAR_FIELD(verbose);
  	COPY_SCALAR_FIELD(freeze_min_age);
  	COPY_SCALAR_FIELD(freeze_table_age);
  	COPY_NODE_FIELD(relation);
--- 2955,2961 ----
  {
  	VacuumStmt *newnode = makeNode(VacuumStmt);
  
! 	COPY_SCALAR_FIELD(options);
  	COPY_SCALAR_FIELD(freeze_min_age);
  	COPY_SCALAR_FIELD(freeze_table_age);
  	COPY_NODE_FIELD(relation);
diff -cpr head/src/backend/nodes/equalfuncs.c work/src/backend/nodes/equalfuncs.c
*** head/src/backend/nodes/equalfuncs.c	2009-10-26 11:26:31.000000000 +0900
--- work/src/backend/nodes/equalfuncs.c	2009-10-27 10:15:06.822375336 +0900
*************** _equalDropdbStmt(DropdbStmt *a, DropdbSt
*** 1482,1491 ****
  static bool
  _equalVacuumStmt(VacuumStmt *a, VacuumStmt *b)
  {
! 	COMPARE_SCALAR_FIELD(vacuum);
! 	COMPARE_SCALAR_FIELD(full);
! 	COMPARE_SCALAR_FIELD(analyze);
! 	COMPARE_SCALAR_FIELD(verbose);
  	COMPARE_SCALAR_FIELD(freeze_min_age);
  	COMPARE_SCALAR_FIELD(freeze_table_age);
  	COMPARE_NODE_FIELD(relation);
--- 1482,1488 ----
  static bool
  _equalVacuumStmt(VacuumStmt *a, VacuumStmt *b)
  {
! 	COMPARE_SCALAR_FIELD(options);
  	COMPARE_SCALAR_FIELD(freeze_min_age);
  	COMPARE_SCALAR_FIELD(freeze_table_age);
  	COMPARE_NODE_FIELD(relation);
diff -cpr head/src/backend/parser/gram.y work/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y	2009-10-15 07:14:22.000000000 +0900
--- work/src/backend/parser/gram.y	2009-10-27 13:23:10.577271273 +0900
*************** static TypeName *TableFuncTypeName(List 
*** 234,239 ****
--- 234,240 ----
  				transaction_mode_item
  
  %type <ival>	opt_lock lock_type cast_context
+ %type <ival>	vacuum_option_list vacuum_option_elem
  %type <boolean>	opt_force opt_or_replace
  				opt_grant_grant_option opt_grant_admin_option
  				opt_nowait opt_if_exists opt_with_data
*************** static TypeName *TableFuncTypeName(List 
*** 489,495 ****
  
  	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
--- 490,496 ----
  
  	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
*************** cluster_index_specification:
*** 6625,6636 ****
  VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->vacuum = true;
! 					n->analyze = false;
! 					n->full = $2;
  					n->freeze_min_age = $3 ? 0 : -1;
  					n->freeze_table_age = $3 ? 0 : -1;
- 					n->verbose = $4;
  					n->relation = NULL;
  					n->va_cols = NIL;
  					$$ = (Node *)n;
--- 6626,6638 ----
  VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->options = VACOPT_VACUUM;
! 					if ($2)
! 						n->options |= VACOPT_FULL;
! 					if ($4)
! 						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = $3 ? 0 : -1;
  					n->freeze_table_age = $3 ? 0 : -1;
  					n->relation = NULL;
  					n->va_cols = NIL;
  					$$ = (Node *)n;
*************** VacuumStmt: VACUUM opt_full opt_freeze o
*** 6638,6649 ****
  			| VACUUM opt_full opt_freeze opt_verbose qualified_name
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->vacuum = true;
! 					n->analyze = false;
! 					n->full = $2;
  					n->freeze_min_age = $3 ? 0 : -1;
  					n->freeze_table_age = $3 ? 0 : -1;
- 					n->verbose = $4;
  					n->relation = $5;
  					n->va_cols = NIL;
  					$$ = (Node *)n;
--- 6640,6652 ----
  			| VACUUM opt_full opt_freeze opt_verbose qualified_name
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->options = VACOPT_VACUUM;
! 					if ($2)
! 						n->options |= VACOPT_FULL;
! 					if ($4)
! 						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = $3 ? 0 : -1;
  					n->freeze_table_age = $3 ? 0 : -1;
  					n->relation = $5;
  					n->va_cols = NIL;
  					$$ = (Node *)n;
*************** VacuumStmt: VACUUM opt_full opt_freeze o
*** 6651,6675 ****
  			| VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
  				{
  					VacuumStmt *n = (VacuumStmt *) $5;
! 					n->vacuum = true;
! 					n->full = $2;
  					n->freeze_min_age = $3 ? 0 : -1;
  					n->freeze_table_age = $3 ? 0 : -1;
- 					n->verbose |= $4;
  					$$ = (Node *)n;
  				}
  		;
  
  AnalyzeStmt:
  			analyze_keyword opt_verbose
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->vacuum = false;
! 					n->analyze = true;
! 					n->full = false;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
- 					n->verbose = $2;
  					n->relation = NULL;
  					n->va_cols = NIL;
  					$$ = (Node *)n;
--- 6654,6709 ----
  			| VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
  				{
  					VacuumStmt *n = (VacuumStmt *) $5;
! 					n->options |= VACOPT_VACUUM;
! 					if ($2)
! 						n->options |= VACOPT_FULL;
! 					if ($4)
! 						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = $3 ? 0 : -1;
  					n->freeze_table_age = $3 ? 0 : -1;
  					$$ = (Node *)n;
  				}
+ 			| VACUUM '(' vacuum_option_list ')'
+ 				{
+ 					VacuumStmt *n = makeNode(VacuumStmt);
+ 					n->options = VACOPT_VACUUM | $3;
+ 					n->relation = NULL;
+ 					n->va_cols = NIL;
+ 					$$ = (Node *) n;
+ 				}
+ 			| VACUUM '(' vacuum_option_list ')' qualified_name opt_name_list
+ 				{
+ 					VacuumStmt *n = makeNode(VacuumStmt);
+ 					n->options = VACOPT_VACUUM | $3;
+ 					n->relation = $5;
+ 					n->va_cols = $6;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ vacuum_option_list:
+ 			vacuum_option_elem								{ $$ = $1; }
+ 			| vacuum_option_list ',' vacuum_option_elem		{ $$ = $1 | $3; }
+ 		;
+ 
+ vacuum_option_elem:
+ 			analyze_keyword		{ $$ = VACOPT_ANALYZE; }
+ 			| VERBOSE			{ $$ = VACOPT_VERBOSE; }
+ 			| FREEZE			{ $$ = VACOPT_FREEZE; }
+ 			| FULL				{ $$ = VACOPT_FULL; }
+ 			| FULL INPLACE		{ $$ = VACOPT_INPLACE; }
+ 			| FULL REPLACE		{ $$ = VACOPT_REPLACE; }
  		;
  
  AnalyzeStmt:
  			analyze_keyword opt_verbose
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->options = VACOPT_ANALYZE;
! 					if ($2)
! 						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
  					n->relation = NULL;
  					n->va_cols = NIL;
  					$$ = (Node *)n;
*************** AnalyzeStmt:
*** 6677,6688 ****
  			| analyze_keyword opt_verbose qualified_name opt_name_list
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->vacuum = false;
! 					n->analyze = true;
! 					n->full = false;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
- 					n->verbose = $2;
  					n->relation = $3;
  					n->va_cols = $4;
  					$$ = (Node *)n;
--- 6711,6721 ----
  			| analyze_keyword opt_verbose qualified_name opt_name_list
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
! 					n->options = VACOPT_ANALYZE;
! 					if ($2)
! 						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
  					n->relation = $3;
  					n->va_cols = $4;
  					$$ = (Node *)n;
*************** unreserved_keyword:
*** 10616,10621 ****
--- 10649,10655 ----
  			| INHERIT
  			| INHERITS
  			| INLINE_P
+ 			| INPLACE
  			| INPUT_P
  			| INSENSITIVE
  			| INSERT
diff -cpr head/src/backend/postmaster/autovacuum.c work/src/backend/postmaster/autovacuum.c
*** head/src/backend/postmaster/autovacuum.c	2009-09-01 04:40:59.000000000 +0900
--- work/src/backend/postmaster/autovacuum.c	2009-10-27 10:15:06.826375552 +0900
*************** autovacuum_do_vac_analyze(autovac_table 
*** 2640,2651 ****
  	MemSet(&vacstmt, 0, sizeof(vacstmt));
  
  	vacstmt.type = T_VacuumStmt;
! 	vacstmt.vacuum = tab->at_dovacuum;
! 	vacstmt.full = false;
! 	vacstmt.analyze = tab->at_doanalyze;
  	vacstmt.freeze_min_age = tab->at_freeze_min_age;
  	vacstmt.freeze_table_age = tab->at_freeze_table_age;
- 	vacstmt.verbose = false;
  	vacstmt.relation = NULL;	/* not used since we pass a relid */
  	vacstmt.va_cols = NIL;
  
--- 2640,2651 ----
  	MemSet(&vacstmt, 0, sizeof(vacstmt));
  
  	vacstmt.type = T_VacuumStmt;
! 	if (tab->at_dovacuum)
! 		vacstmt.options |= VACOPT_VACUUM;
! 	if (tab->at_doanalyze)
! 		vacstmt.options |= VACOPT_ANALYZE;
  	vacstmt.freeze_min_age = tab->at_freeze_min_age;
  	vacstmt.freeze_table_age = tab->at_freeze_table_age;
  	vacstmt.relation = NULL;	/* not used since we pass a relid */
  	vacstmt.va_cols = NIL;
  
diff -cpr head/src/backend/tcop/utility.c work/src/backend/tcop/utility.c
*** head/src/backend/tcop/utility.c	2009-10-26 11:26:40.000000000 +0900
--- work/src/backend/tcop/utility.c	2009-10-27 10:51:53.562278044 +0900
*************** CreateCommandTag(Node *parsetree)
*** 1815,1821 ****
  			break;
  
  		case T_VacuumStmt:
! 			if (((VacuumStmt *) parsetree)->vacuum)
  				tag = "VACUUM";
  			else
  				tag = "ANALYZE";
--- 1815,1821 ----
  			break;
  
  		case T_VacuumStmt:
! 			if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)
  				tag = "VACUUM";
  			else
  				tag = "ANALYZE";
diff -cpr 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-10-27 11:26:14.373277502 +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 -cpr head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h	2009-10-26 11:26:41.000000000 +0900
--- work/src/include/nodes/parsenodes.h	2009-10-27 13:23:10.700283894 +0900
*************** typedef struct ClusterStmt
*** 2204,2216 ****
   * just one node type for both.
   * ----------------------
   */
  typedef struct VacuumStmt
  {
  	NodeTag		type;
! 	bool		vacuum;			/* do VACUUM step */
! 	bool		full;			/* do FULL (non-concurrent) vacuum */
! 	bool		analyze;		/* do ANALYZE step */
! 	bool		verbose;		/* print progress info */
  	int			freeze_min_age; /* min freeze age, or -1 to use default */
  	int			freeze_table_age;		/* age at which to scan whole table */
  	RangeVar   *relation;		/* single table to process, or NULL */
--- 2204,2226 ----
   * just one node type for both.
   * ----------------------
   */
+ 
+ typedef enum VacuumOption
+ {
+ 	VACOPT_VACUUM		= 1 << 0,	/* do VACUUM */
+ 	VACOPT_ANALYZE		= 1 << 1,	/* do ANALYZE */
+ 	VACOPT_VERBOSE		= 1 << 2,	/* VERBOSE option */
+ 	VACOPT_FREEZE		= 1 << 3,	/* FREEZE option */
+ 	VACOPT_INPLACE		= 1 << 4,	/* traditional VACUUM FULL */
+ 	VACOPT_REPLACE		= 1 << 5,	/* rewrite-version of VACUUM FULL */
+ 
+ 	VACOPT_FULL			= VACOPT_REPLACE	/* default is REPLACE */
+ } VacuumOption;
+ 
  typedef struct VacuumStmt
  {
  	NodeTag		type;
! 	bits32		options;		/* OR of VacuumOption flags */
  	int			freeze_min_age; /* min freeze age, or -1 to use default */
  	int			freeze_table_age;		/* age at which to scan whole table */
  	RangeVar   *relation;		/* single table to process, or NULL */
diff -cpr head/src/include/parser/kwlist.h work/src/include/parser/kwlist.h
*** head/src/include/parser/kwlist.h	2009-10-13 05:39:42.000000000 +0900
--- work/src/include/parser/kwlist.h	2009-10-27 13:16:20.770282670 +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)