*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
***************
*** 17,24 ****
--- 17,26 ----
  
  #include "catalog/pg_type.h"
  #include "nodes/makefuncs.h"
+ #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  
+ static bool parseBooleanOption(DefElem *opt);
  
  /*
   * makeA_Expr -
***************
*** 385,387 **** makeDefElemExtended(char *namespace, char *name, Node *arg,
--- 387,453 ----
  
  	return res;
  }
+ 
+ /*
+  * makeExplain -
+  *  build an ExplainStmt node by parsing the generic options list
+  */
+ ExplainStmt *
+ makeExplain(List *options, Node *query)
+ {
+ 	ExplainStmt *n = makeNode(ExplainStmt);
+ 	ListCell *lc;
+ 
+ 	foreach (lc, options)
+ 	{
+ 		DefElem *opt = lfirst(lc);
+ 		if (!strcmp(opt->defname, "analyze"))
+ 			n->analyze = parseBooleanOption(opt);
+ 		else if (!strcmp(opt->defname, "verbose"))
+ 			n->verbose = parseBooleanOption(opt);
+ 		else
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_PARAMETER),
+ 				 errmsg("unknown EXPLAIN option: %s", opt->defname)));
+ 	}
+ 
+ 	n->query = query;
+ 	return n;
+ }
+ 
+ /*
+  * parseBooleanOption -
+  * 	Interpret a DefElem option as a boolean.
+  */
+ static bool
+ parseBooleanOption(DefElem *opt)
+ {
+ 	bool res;
+ 
+ 	/*
+  	 * We interpret an omitted boolean argument as equivalent to "true", so
+  	 * that, for example, EXPLAIN (ANALYZE) means the same thing as
+  	 * EXPLAIN (ANALYZE ON).
+  	 */
+ 	if (!opt->arg)
+ 	{
+ 		return true;
+ 	}
+ 	else if (IsA(opt->arg, Integer))
+ 	{
+ 		if (intVal(opt->arg) == 0)
+ 			return false;
+ 		else if (intVal(opt->arg) == 1)
+ 			return true;
+ 	}
+ 	else if (IsA(opt->arg, String))
+ 	{
+ 		if (parse_bool(strVal(opt->arg), &res))
+ 			return res;
+ 	}
+ 
+ 	ereport(ERROR,
+ 		(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 		 errmsg("parameter \"%s\" requires a Boolean value",
+ 			opt->defname)));
+ }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 366,371 **** static TypeName *TableFuncTypeName(List *columns);
--- 366,376 ----
  %type <defelt>	generic_option_elem alter_generic_option_elem
  %type <list>	generic_option_list alter_generic_option_list
  
+ %type <str>		explain_option_name
+ %type <node>	explain_option_arg
+ %type <defelt>	explain_option_elem
+ %type <list>	explain_option_list
+ 
  %type <typnam>	Typename SimpleTypename ConstTypename
  				GenericType Numeric opt_float
  				Character ConstCharacter
***************
*** 6441,6457 **** opt_name_list:
   *
   *		QUERY:
   *				EXPLAIN [ANALYZE] [VERBOSE] query
   *
   *****************************************************************************/
  
  ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
  				{
! 					ExplainStmt *n = makeNode(ExplainStmt);
  					n->analyze = $2;
  					n->verbose = $3;
- 					n->query = $4;
  					$$ = (Node *)n;
  				}
  		;
  
  ExplainableStmt:
--- 6446,6466 ----
   *
   *		QUERY:
   *				EXPLAIN [ANALYZE] [VERBOSE] query
+  *				EXPLAIN ( options ) query
   *
   *****************************************************************************/
  
  ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
  				{
! 					ExplainStmt *n = makeExplain(NIL, (Node *) $4);
  					n->analyze = $2;
  					n->verbose = $3;
  					$$ = (Node *)n;
  				}
+ 		|	EXPLAIN '(' explain_option_list ')' ExplainableStmt
+ 				{
+ 					$$ = (Node *) makeExplain((List *) $3, (Node *) $5);
+ 				}
  		;
  
  ExplainableStmt:
***************
*** 6464,6472 **** ExplainableStmt:
  			| ExecuteStmt					/* by default all are $$=$1 */
  		;
  
  opt_analyze:
  			analyze_keyword			{ $$ = TRUE; }
! 			| /* EMPTY */			{ $$ = FALSE; }
  		;
  
  /*****************************************************************************
--- 6473,6523 ----
  			| ExecuteStmt					/* by default all are $$=$1 */
  		;
  
+ /*
+  * The precedence declaration for the opt_analyze EMPTY case, below, is
+  * necessary to prevent a shift/reduce conflict in the second production for
+  * ExplainStmt, above.  Otherwise, when the parser encounters "EXPLAIN (", it
+  * can't tell whether the "(" is the beginning of a SelectStmt or the beginning
+  * of the options list.  The precedence declaration below forces the latter
+  * interpretation.
+  *
+  * It might seem that we could get away with simply changing the definition of
+  * ExplainableStmt to use select_without_parens rather than SelectStmt, but
+  * that does not work, because select_without_parens produces expressions such
+  * as "(SELECT NULL) ORDER BY 1" that we interpret as legal queries.
+  */
  opt_analyze:
  			analyze_keyword			{ $$ = TRUE; }
! 			| /* EMPTY */			%prec UMINUS { $$ = FALSE; }
! 		;
! 
! explain_option_list:
! 			explain_option_elem
! 				{
! 					$$ = list_make1($1);
! 				}
! 			| explain_option_list ',' explain_option_elem
! 				{
! 					$$ = lappend($1, $3);
! 				}
! 		;
! 
! explain_option_elem:
! 			explain_option_name explain_option_arg
! 				{
! 					$$ = makeDefElem($1, $2);
! 				}
! 		;
! 
! explain_option_name:
! 				ColLabel			{ $$ = $1; }
! 		;
! 
! explain_option_arg:
! 			  opt_boolean			{ $$ = (Node *) makeString($1); }
! 			| ColId_or_Sconst		{ $$ = (Node *) makeString($1); }
! 			| SignedIconst			{ $$ = (Node *) makeInteger($1); }
! 			| /* EMPTY */			{ $$ = NULL; }
  		;
  
  /*****************************************************************************
*** a/src/include/nodes/makefuncs.h
--- b/src/include/nodes/makefuncs.h
***************
*** 69,72 **** extern DefElem *makeDefElem(char *name, Node *arg);
--- 69,74 ----
  extern DefElem *makeDefElemExtended(char *namespace, char *name, Node *arg,
  					DefElemAction defaction);
  
+ extern ExplainStmt *makeExplain(List *options, Node *query);
+ 
  #endif   /* MAKEFUNC_H */