Here's an updated version of my "generic options for explain" patch.

http://archives.postgresql.org/message-id/603c8f070905231747j2e099c23hef8eafbf26682...@mail.gmail.com

This is rebased to CVS HEAD, post pgindent.  Also, I've made a few
changes to the syntax.  Previously, I suggested this syntax:

explain (analyze 'on', verbose 'off') query...

Greg Stark suggested that for boolean parameters we might allow the
argument to be omitted and default to true.

http://archives.postgresql.org/message-id/4c72394c-384f-4fc4-be3e-8f556fbd3...@enterprisedb.com

That seemed like a good idea so I did it.  I also took it a step
further and did something we've done elsewhere in the parser so that
in many cases the quotes aren't even necessary.  Thus you can now also
write:

explain (analyze on, verbose off) query...

Of course, we don't have any consensus on the right syntax, but my
previous proposal had at least as much support as any of the other
alternatives that were offered, so we'll see where we get with this
one.

Followup patch that adds actual new functionality coming shortly.

...Robert
*** 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 */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to