diff -cprN head/doc/src/sgml/ref/vacuum.sgml work/doc/src/sgml/ref/vacuum.sgml
*** head/doc/src/sgml/ref/vacuum.sgml	2009-03-25 05:17:08.000000000 +0900
--- work/doc/src/sgml/ref/vacuum.sgml	2009-11-13 10:17:39.141536000 +0900
*************** PostgreSQL documentation
*** 21,26 ****
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
+ VACUUM [ ( { FULL | 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>
diff -cprN 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-11-13 10:00:02.275841676 +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 -cprN head/src/backend/commands/vacuum.c work/src/backend/commands/vacuum.c
*** head/src/backend/commands/vacuum.c	2009-11-11 03:00:06.000000000 +0900
--- work/src/backend/commands/vacuum.c	2009-11-13 10:33:33.200912128 +0900
*************** 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;
--- 289,310 ----
  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;
  
! 	/* sanity checks */
! 	Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
! 	Assert(!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)) ||
! 			(vacstmt->options & VACOPT_VACUUM));
! 	Assert(vacstmt->va_cols == NIL || (vacstmt->options & VACOPT_ANALYZE));
! 
! 	stmttype = (vacstmt->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
! 
! 	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
--- 323,344 ----
  	 *
  	 * 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)
--- 386,396 ----
  	 * 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;
  
--- 446,456 ----
  			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.
--- 510,516 ----
  		StartTransactionCommand();
  	}
  
! 	if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
  	{
  		/*
  		 * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1034,1040 ****
  	 */
  	PushActiveSnapshot(GetTransactionSnapshot());
  
! 	if (!vacstmt->full)
  	{
  		/*
  		 * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
--- 1042,1048 ----
  	 */
  	PushActiveSnapshot(GetTransactionSnapshot());
  
! 	if (!(vacstmt->options & VACOPT_FULL))
  	{
  		/*
  		 * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1074,1080 ****
  	 * 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.
--- 1082,1090 ----
  	 * 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_FULL)
! 				? AccessExclusiveLock
! 				: ShareUpdateExclusiveLock;
  
  	/*
  	 * Open the relation and get the appropriate lock on it.
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1186,1192 ****
  	/*
  	 * Do the actual work --- either FULL or "lazy" vacuum
  	 */
! 	if (vacstmt->full)
  		heldoff = full_vacuum_rel(onerel, vacstmt);
  	else
  		heldoff = lazy_vacuum_rel(onerel, vacstmt, vac_strategy, scanned_all);
--- 1196,1202 ----
  	/*
  	 * 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);
*************** full_vacuum_rel(Relation onerel, VacuumS
*** 1332,1338 ****
  
  	/* report results to the stats collector, too */
  	pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
! 						 true, vacstmt->analyze, vacrelstats->rel_tuples);
  
  	return heldoff;
  }
--- 1342,1348 ----
  
  	/* 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);
  
  	return heldoff;
  }
diff -cprN head/src/backend/commands/vacuumlazy.c work/src/backend/commands/vacuumlazy.c
*** head/src/backend/commands/vacuumlazy.c	2009-11-11 03:00:06.000000000 +0900
--- work/src/backend/commands/vacuumlazy.c	2009-11-13 10:00:02.277828910 +0900
*************** lazy_vacuum_rel(Relation onerel, VacuumS
*** 164,170 ****
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration > 0)
  		starttime = GetCurrentTimestamp();
  
! 	if (vacstmt->verbose)
  		elevel = INFO;
  	else
  		elevel = DEBUG2;
--- 164,170 ----
  	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
*** 236,242 ****
  	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)
--- 236,243 ----
  	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 -cprN head/src/backend/nodes/copyfuncs.c work/src/backend/nodes/copyfuncs.c
*** head/src/backend/nodes/copyfuncs.c	2009-10-28 23:55:38.000000000 +0900
--- work/src/backend/nodes/copyfuncs.c	2009-11-13 10:00:02.278845497 +0900
*************** _copyVacuumStmt(VacuumStmt *from)
*** 2957,2966 ****
  {
  	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);
--- 2957,2963 ----
  {
  	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 -cprN head/src/backend/nodes/equalfuncs.c work/src/backend/nodes/equalfuncs.c
*** head/src/backend/nodes/equalfuncs.c	2009-10-28 23:55:38.000000000 +0900
--- work/src/backend/nodes/equalfuncs.c	2009-11-13 10:00:02.279860288 +0900
*************** _equalDropdbStmt(DropdbStmt *a, DropdbSt
*** 1483,1492 ****
  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);
--- 1483,1489 ----
  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 -cprN head/src/backend/parser/gram.y work/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y	2009-11-12 05:31:26.000000000 +0900
--- work/src/backend/parser/gram.y	2009-11-13 10:29:38.737883224 +0900
*************** static TypeName *TableFuncTypeName(List 
*** 229,234 ****
--- 229,235 ----
  				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 
*** 484,490 ****
  
  	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
*************** 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,6717 ----
  			| 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;
+ 					if (n->options & VACOPT_FREEZE)
+ 						n->freeze_min_age = n->freeze_table_age = 0;
+ 					else
+ 						n->freeze_min_age = n->freeze_table_age = -1;
+ 					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;
+ 					if (n->options & VACOPT_FREEZE)
+ 						n->freeze_min_age = n->freeze_table_age = 0;
+ 					else
+ 						n->freeze_min_age = n->freeze_table_age = -1;
+ 					n->relation = $5;
+ 					n->va_cols = $6;
+ 					if (n->va_cols != NIL)	/* implies analyze */
+ 						n->options |= VACOPT_ANALYZE;
+ 					$$ = (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; }
  		;
  
  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;
--- 6719,6729 ----
  			| 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;
diff -cprN 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-11-13 10:00:02.283848658 +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 -cprN 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-11-13 10:00:02.284847777 +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 -cprN head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h	2009-11-07 06:57:57.000000000 +0900
--- work/src/include/nodes/parsenodes.h	2009-11-13 10:04:59.222848463 +0900
*************** typedef struct ClusterStmt
*** 2212,2224 ****
   * 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 */
--- 2212,2231 ----
   * 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_FULL			= 1 << 4	/* FULL option */
+ } 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 -cprN head/src/test/regress/expected/vacuum.out work/src/test/regress/expected/vacuum.out
*** head/src/test/regress/expected/vacuum.out	2002-07-20 13:58:14.000000000 +0900
--- work/src/test/regress/expected/vacuum.out	2009-11-13 10:15:10.348824551 +0900
*************** SELECT * FROM vactst;
*** 56,59 ****
--- 56,62 ----
  ---
  (0 rows)
  
+ VACUUM (FULL) vactst;
+ VACUUM (FREEZE) vactst;
+ VACUUM (FULL, FREEZE) vactst;
  DROP TABLE vactst;
diff -cprN head/src/test/regress/sql/vacuum.sql work/src/test/regress/sql/vacuum.sql
*** head/src/test/regress/sql/vacuum.sql	2002-07-20 13:58:14.000000000 +0900
--- work/src/test/regress/sql/vacuum.sql	2009-11-13 10:14:15.709830767 +0900
*************** VACUUM FULL vactst;
*** 39,42 ****
--- 39,46 ----
  DELETE FROM vactst;
  SELECT * FROM vactst;
  
+ VACUUM (FULL) vactst;
+ VACUUM (FREEZE) vactst;
+ VACUUM (FULL, FREEZE) vactst;
+ 
  DROP TABLE vactst;