On Mon, 2008-11-10 at 18:02 +0900, ITAGAKI Takahiro wrote:
> That's because explain.log_analyze requires executor instruments,
> and it's not free. I think we'd better to have an option to avoid
> the overheads... Oops, I found my bug when force_instrument is
> turned on. It should be enabled only when
> (explain_log_min_duration >= 0 && explain_log_analyze).
> 
> I send a new patch to fix it. A documentation about
> explain.log_nested_statements is also added to the sgml file.
> 

Great. I attached a patch with some minor documentation changes.

There seems to be no auto_explain view, so I assumed that section of the
docs was old, and I removed it. Let me know if you intend to include the
view.

Thanks! This patch is ready to go, as far as I'm concerned.

Regards,
        Jeff Davis
diff --git a/contrib/Makefile b/contrib/Makefile
index 30f75c7..b25af3c 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -6,6 +6,7 @@ include $(top_builddir)/src/Makefile.global
 
 WANTED_DIRS = \
 		adminpack	\
+		auto_explain	\
 		btree_gist	\
 		chkpass		\
 		citext		\
diff --git a/contrib/auto_explain/Makefile b/contrib/auto_explain/Makefile
new file mode 100644
index 0000000..0d0ccc2
--- /dev/null
+++ b/contrib/auto_explain/Makefile
@@ -0,0 +1,13 @@
+MODULE_big = auto_explain
+OBJS = auto_explain.o
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/auto_explain
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
new file mode 100644
index 0000000..af994e7
--- /dev/null
+++ b/contrib/auto_explain/auto_explain.c
@@ -0,0 +1,215 @@
+/*
+ * auto_explain.c
+ */
+#include "postgres.h"
+
+#include "commands/explain.h"
+#include "executor/instrument.h"
+#include "miscadmin.h"
+#include "utils/guc_tables.h"
+
+PG_MODULE_MAGIC;
+
+#define GUCNAME(name)		("explain." name)
+
+static int		explain_log_min_duration = -1;	/* msec or -1 */
+static bool		explain_log_analyze = false;
+static bool		explain_log_verbose = false;
+static bool		explain_log_nested = false;
+
+static bool		toplevel = true;
+static ExecutorRun_hook_type	prev_ExecutorRun = NULL;
+
+void	_PG_init(void);
+void	_PG_fini(void);
+
+static void explain_ExecutorRun(QueryDesc *queryDesc,
+								ScanDirection direction,
+								long count);
+static bool assign_log_min_duration(int newval, bool doit, GucSource source);
+static bool assign_log_analyze(bool newval, bool doit, GucSource source);
+static bool assign_log_verbose(bool newval, bool doit, GucSource source);
+static bool assign_log_nested(bool newval, bool doit, GucSource source);
+
+static struct config_int def_log_min_duration =
+{
+	{
+		GUCNAME("log_min_duration"),
+		PGC_USERSET,
+		STATS_MONITORING,
+		"Sets the minimum execution time above which plans will be logged.",
+		"Zero prints all plans. -1 turns this feature off.",
+		GUC_UNIT_MS
+	},
+	&explain_log_min_duration,
+	-1, -1, INT_MAX / 1000, assign_log_min_duration, NULL
+};
+
+static struct config_bool def_log_analyze =
+{
+	{
+		GUCNAME("log_analyze"),
+		PGC_USERSET,
+		STATS_MONITORING,
+		"Use EXPLAIN ANALYZE for plan logging."
+	},
+	&explain_log_analyze,
+	false, assign_log_analyze, NULL
+};
+
+static struct config_bool def_log_verbose =
+{
+	{
+		GUCNAME("log_verbose"),
+		PGC_USERSET,
+		STATS_MONITORING,
+		"Use EXPLAIN VERBOSE for plan logging."
+	},
+	&explain_log_verbose,
+	false, assign_log_verbose, NULL
+};
+
+static struct config_bool def_log_nested_statements =
+{
+	{
+		GUCNAME("log_nested_statements"),
+		PGC_USERSET,
+		STATS_MONITORING,
+		"Log nested statements."
+	},
+	&explain_log_nested,
+	false, assign_log_nested, NULL
+};
+
+void
+_PG_init(void)
+{
+	DefineCustomVariable(PGC_INT, &def_log_min_duration);
+	DefineCustomVariable(PGC_BOOL, &def_log_analyze);
+	DefineCustomVariable(PGC_BOOL, &def_log_verbose);
+	DefineCustomVariable(PGC_BOOL, &def_log_nested_statements);
+
+	/* install ExecutorRun_hook */
+	prev_ExecutorRun = ExecutorRun_hook;
+	ExecutorRun_hook = explain_ExecutorRun;
+}
+
+void
+_PG_fini(void)
+{
+	/* uninstall ExecutorRun_hook */
+	ExecutorRun_hook = prev_ExecutorRun;
+}
+
+void
+explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
+{
+	if ((toplevel || explain_log_nested) && explain_log_min_duration >= 0)
+	{
+		instr_time		starttime;
+		instr_time		duration;
+		double			msec;
+
+		/* Disable our hooks temporarily during the top-level query. */
+		toplevel = false;
+		PG_TRY();
+		{
+			INSTR_TIME_SET_CURRENT(starttime);
+
+			if (prev_ExecutorRun)
+				prev_ExecutorRun(queryDesc, direction, count);
+			else
+				standard_ExecutorRun(queryDesc, direction, count);
+
+			INSTR_TIME_SET_CURRENT(duration);
+			INSTR_TIME_SUBTRACT(duration, starttime);
+			msec = INSTR_TIME_GET_MILLISEC(duration);
+
+			/* Log plan if duration is exceeded. */
+			if (msec > explain_log_min_duration)
+			{
+				StringInfoData	buf;
+
+				initStringInfo(&buf);
+				ExplainOneResult(&buf, queryDesc,
+					queryDesc->doInstrument && explain_log_analyze,
+					explain_log_verbose);
+
+				/* Remove last line break */
+				if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
+				{
+					buf.data[buf.len - 1] = '\0';
+					buf.len--;
+				}
+				ereport(LOG,
+						(errmsg("duration: %.3f ms  plan:\n%s",
+								msec, buf.data)));
+
+				pfree(buf.data);
+			}
+
+			toplevel = true;
+		}
+		PG_CATCH();
+		{
+			toplevel = true;
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+	}
+	else
+	{
+		/* ignore recursive executions, that are typically function calls */
+		if (prev_ExecutorRun)
+			prev_ExecutorRun(queryDesc, direction, count);
+		else
+			standard_ExecutorRun(queryDesc, direction, count);
+	}
+}
+
+/* Emulate PGC_SUSET for custom variables. */
+static bool
+suset_assign(GucSource source, const char *name)
+{
+	if (source >= PGC_S_CLIENT && !superuser())
+	{
+		ereport(GUC_complaint_elevel(source),
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied to set parameter \"%s\"", name)));
+		return false;
+	}
+
+	return true;
+}
+
+static bool
+assign_log_min_duration(int newval, bool doit, GucSource source)
+{
+	if (!suset_assign(source, GUCNAME("log_min_duration")))
+		return false;
+	if (doit)
+		force_instrument = (newval >= 0 && explain_log_analyze);
+	return true;
+}
+
+static bool
+assign_log_analyze(bool newval, bool doit, GucSource source)
+{
+	if (!suset_assign(source, GUCNAME("log_analyze")))
+		return false;
+	if (doit)
+		force_instrument = (explain_log_min_duration >= 0 && newval);
+	return true;
+}
+
+static bool
+assign_log_verbose(bool newval, bool doit, GucSource source)
+{
+	return suset_assign(source, GUCNAME("log_verbose"));
+}
+
+static bool
+assign_log_nested(bool newval, bool doit, GucSource source)
+{
+	return suset_assign(source, GUCNAME("log_nested_statements"));
+}
diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml
new file mode 100644
index 0000000..94b7daf
--- /dev/null
+++ b/doc/src/sgml/auto-explain.sgml
@@ -0,0 +1,141 @@
+<sect1 id="autoexplain">
+ <title>auto_explain</title>
+
+ <indexterm zone="autoexplain">
+  <primary>auto_explain</primary>
+ </indexterm>
+
+ <para>
+  The <filename>auto_explain</filename> module provides a means for
+  logging execution plans automatically, without having to run <command>EXPLAIN</>.
+ </para>
+
+ <para>
+  You can <command>LOAD</> this module dynamically or preload it automatically with
+  <varname>shared_preload_libraries</> or <varname>local_preload_libraries</>.
+ </para>
+
+ <sect2>
+  <title>Configuration parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term>
+     <varname>explain.log_min_duration</varname> (<type>integer</type>)
+    </term>
+    <listitem>
+     <para>
+      <varname>explain.log_min_duration</varname> is the minimum execution time
+      in milliseconds that will cause the plan to be logged. Setting this to zero
+      logs all plans. Minus-one (the default) disables logging of plans.
+      For example, if you set it to <literal>250ms</literal> then all plans
+      that run 250ms or longer in executor will be logged.
+      Enabling this parameter can be helpful in tracking down unoptimized queries
+      in your applications. Only superusers can change this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <varname>explain.log_analyze</varname> (<type>boolean</type>)
+    </term>
+
+    <listitem>
+     <para>
+      <varname>explain.log_analyze</varname> causes <command>EXPLAIN ANALYZE</>
+      to be used rather than normal <command>EXPLAIN</> when an execution plan
+      is logged. This parameter is off by default. Only superusers can change
+      this setting.
+     </para>
+     <para>
+      NOTE: If you set the parameter on, instrument timers are enabled even if
+      you don't use EXPLAIN ANALYZE. This creates some overhead for all
+      statements executed.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <varname>explain.log_verbose</varname> (<type>boolean</type>)
+    </term>
+
+    <listitem>
+     <para>
+      <varname>explain.log_verbose</varname> causes <command>EXPLAIN VERBOSE</>
+      to be used rather than normal <command>EXPLAIN</> when an execution plan
+      is logged. This parameter is off by default. Only superusers can change
+      this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <varname>explain.log_nested_statements</varname> (<type>boolean</type>)
+    </term>
+
+    <listitem>
+     <para>
+      <varname>explain.log_nested_statements</varname> causes the execution plans
+      of nested statements (statements executed inside a function) to be logged
+      as well. If the value is off, only top-level plans are logged. This
+      parameter is off by default. Only superusers can change this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>
+   If you set these explain.* parameters in your postgresql.conf,
+   you also need to add 'explain' in <varname>custom_variable_classes</>.
+  </para>
+
+  <programlisting>
+  # postgresql.conf
+  shared_preload_libraries = 'auto_explain'
+  
+  custom_variable_classes = 'explain'
+  explain.log_min_duration = 3s
+  </programlisting>
+ </sect2>
+
+ <sect2>
+  <title>Example</title>
+
+  <programlisting>
+  postgres=# LOAD 'auto_explain';
+  postgres=# SET explain.log_min_duration = 0;
+  postgres=# SELECT count(*)
+               FROM pg_class, pg_index
+              WHERE oid = indrelid AND indisunique;
+  </programlisting>
+ </sect2>
+
+ <sect2>
+  <title>Sample Log Output</title>
+  <programlisting>
+  LOG:  duration: 0.986 ms  plan:
+          Aggregate  (cost=14.90..14.91 rows=1 width=0)
+            ->  Hash Join  (cost=3.91..14.70 rows=81 width=0)
+                  Hash Cond: (pg_class.oid = pg_index.indrelid)
+                  ->  Seq Scan on pg_class  (cost=0.00..8.27 rows=227 width=4)
+                  ->  Hash  (cost=2.90..2.90 rows=81 width=4)
+                        ->  Seq Scan on pg_index  (cost=0.00..2.90 rows=81 width=4)
+                              Filter: indisunique
+  STATEMENT:  SELECT count(*)
+            FROM pg_class, pg_index
+           WHERE oid = indrelid AND indisunique;
+  </programlisting>
+ </sect2>
+
+ <sect2>
+  <title>Authors</title>
+
+  <para>
+   Takahiro Itagaki <email>[EMAIL PROTECTED]</email>
+  </para>
+ </sect2>
+
+</sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 72d8828..0108da3 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -79,6 +79,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
  </para>
 
  &adminpack;
+ &auto-explain;
  &btree-gist;
  &chkpass;
  &citext;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index b0538b6..68db2b9 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -92,6 +92,7 @@
 <!-- contrib information -->
 <!entity contrib         SYSTEM "contrib.sgml">
 <!entity adminpack       SYSTEM "adminpack.sgml">
+<!entity auto-explain    SYSTEM "auto-explain.sgml">
 <!entity btree-gist      SYSTEM "btree-gist.sgml">
 <!entity chkpass         SYSTEM "chkpass.sgml">
 <!entity citext          SYSTEM "citext.sgml">
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c0c53cf..4d46a34 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -224,7 +224,6 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
 	QueryDesc  *queryDesc;
 	instr_time	starttime;
 	double		totaltime = 0;
-	ExplainState *es;
 	StringInfoData buf;
 	int			eflags;
 
@@ -265,17 +264,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
 		totaltime += elapsed_time(&starttime);
 	}
 
-	es = (ExplainState *) palloc0(sizeof(ExplainState));
-
-	es->printTList = stmt->verbose;
-	es->printAnalyze = stmt->analyze;
-	es->pstmt = queryDesc->plannedstmt;
-	es->rtable = queryDesc->plannedstmt->rtable;
-
 	initStringInfo(&buf);
-	explain_outNode(&buf,
-					queryDesc->plannedstmt->planTree, queryDesc->planstate,
-					NULL, 0, es);
+	ExplainOneResult(&buf, queryDesc, stmt->analyze, stmt->verbose);
 
 	/*
 	 * If we ran the command, run any AFTER triggers it queued.  (Note this
@@ -290,7 +280,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
 	}
 
 	/* Print info about runtime of triggers */
-	if (es->printAnalyze)
+	if (stmt->analyze)
 	{
 		ResultRelInfo *rInfo;
 		bool		show_relname;
@@ -335,7 +325,26 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
 	do_text_output_multiline(tstate, buf.data);
 
 	pfree(buf.data);
-	pfree(es);
+}
+
+/*
+ * ExplainOneResult -
+ *	  converts a Plan node into ascii string and appends it to 'str'
+ */
+void
+ExplainOneResult(StringInfo str, QueryDesc *queryDesc,
+				 bool analyze, bool verbose)
+{
+	ExplainState	es = { 0 };
+
+	es.printTList = verbose;
+	es.printAnalyze = analyze;
+	es.pstmt = queryDesc->plannedstmt;
+	es.rtable = queryDesc->plannedstmt->rtable;
+
+	explain_outNode(str,
+					queryDesc->plannedstmt->planTree, queryDesc->planstate,
+					NULL, 0, &es);
 }
 
 /*
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index a933d3c..ff2df9b 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -32,7 +32,7 @@
  * if there are several).
  */
 Portal		ActivePortal = NULL;
-
+bool		force_instrument = false;
 
 static void ProcessQuery(PlannedStmt *plan,
 			 ParamListInfo params,
@@ -76,7 +76,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
 	qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot);
 	qd->dest = dest;			/* output dest */
 	qd->params = params;		/* parameter values passed into query */
-	qd->doInstrument = doInstrument;	/* instrumentation wanted? */
+	qd->doInstrument = force_instrument || doInstrument;	/* instrumentation wanted? */
 
 	/* null these fields until set by ExecutorStart */
 	qd->tupDesc = NULL;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fd7543b..f8a7e64 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -169,6 +169,7 @@ static const char *assign_pgstat_temp_directory(const char *newval, bool doit, G
 
 static char *config_enum_get_options(struct config_enum *record, 
 									 const char *prefix, const char *suffix);
+static void initialize_option(struct config_generic *gconf);
 
 
 /*
@@ -3194,112 +3195,7 @@ InitializeGUCOptions(void)
 	for (i = 0; i < num_guc_variables; i++)
 	{
 		struct config_generic *gconf = guc_variables[i];
-
-		gconf->status = 0;
-		gconf->reset_source = PGC_S_DEFAULT;
-		gconf->source = PGC_S_DEFAULT;
-		gconf->stack = NULL;
-		gconf->sourcefile = NULL;
-		gconf->sourceline = 0;
-
-		switch (gconf->vartype)
-		{
-			case PGC_BOOL:
-				{
-					struct config_bool *conf = (struct config_bool *) gconf;
-
-					if (conf->assign_hook)
-						if (!(*conf->assign_hook) (conf->boot_val, true,
-												   PGC_S_DEFAULT))
-							elog(FATAL, "failed to initialize %s to %d",
-								 conf->gen.name, (int) conf->boot_val);
-					*conf->variable = conf->reset_val = conf->boot_val;
-					break;
-				}
-			case PGC_INT:
-				{
-					struct config_int *conf = (struct config_int *) gconf;
-
-					Assert(conf->boot_val >= conf->min);
-					Assert(conf->boot_val <= conf->max);
-					if (conf->assign_hook)
-						if (!(*conf->assign_hook) (conf->boot_val, true,
-												   PGC_S_DEFAULT))
-							elog(FATAL, "failed to initialize %s to %d",
-								 conf->gen.name, conf->boot_val);
-					*conf->variable = conf->reset_val = conf->boot_val;
-					break;
-				}
-			case PGC_REAL:
-				{
-					struct config_real *conf = (struct config_real *) gconf;
-
-					Assert(conf->boot_val >= conf->min);
-					Assert(conf->boot_val <= conf->max);
-					if (conf->assign_hook)
-						if (!(*conf->assign_hook) (conf->boot_val, true,
-												   PGC_S_DEFAULT))
-							elog(FATAL, "failed to initialize %s to %g",
-								 conf->gen.name, conf->boot_val);
-					*conf->variable = conf->reset_val = conf->boot_val;
-					break;
-				}
-			case PGC_STRING:
-				{
-					struct config_string *conf = (struct config_string *) gconf;
-					char	   *str;
-
-					*conf->variable = NULL;
-					conf->reset_val = NULL;
-
-					if (conf->boot_val == NULL)
-					{
-						/* leave the value NULL, do not call assign hook */
-						break;
-					}
-
-					str = guc_strdup(FATAL, conf->boot_val);
-					conf->reset_val = str;
-
-					if (conf->assign_hook)
-					{
-						const char *newstr;
-
-						newstr = (*conf->assign_hook) (str, true,
-													   PGC_S_DEFAULT);
-						if (newstr == NULL)
-						{
-							elog(FATAL, "failed to initialize %s to \"%s\"",
-								 conf->gen.name, str);
-						}
-						else if (newstr != str)
-						{
-							free(str);
-
-							/*
-							 * See notes in set_config_option about casting
-							 */
-							str = (char *) newstr;
-							conf->reset_val = str;
-						}
-					}
-					*conf->variable = str;
-					break;
-				}
-			case PGC_ENUM:
-				{
-					struct config_enum *conf = (struct config_enum *) gconf;
-
-					if (conf->assign_hook)
-						if (!(*conf->assign_hook) (conf->boot_val, true,
-												   PGC_S_DEFAULT))
-							elog(FATAL, "failed to initialize %s to %s",
-								 conf->gen.name, 
-								 config_enum_lookup_by_value(conf, conf->boot_val));
-					*conf->variable = conf->reset_val = conf->boot_val;
-					break;
-				}
-		}
+		initialize_option(gconf);
 	}
 
 	guc_dirty = false;
@@ -3355,6 +3251,115 @@ InitializeGUCOptions(void)
 	}
 }
 
+static void
+initialize_option(struct config_generic *gconf)
+{
+	gconf->status = 0;
+	gconf->reset_source = PGC_S_DEFAULT;
+	gconf->source = PGC_S_DEFAULT;
+	gconf->stack = NULL;
+	gconf->sourcefile = NULL;
+	gconf->sourceline = 0;
+
+	switch (gconf->vartype)
+	{
+		case PGC_BOOL:
+			{
+				struct config_bool *conf = (struct config_bool *) gconf;
+
+				if (conf->assign_hook)
+					if (!(*conf->assign_hook) (conf->boot_val, true,
+											   PGC_S_DEFAULT))
+						elog(FATAL, "failed to initialize %s to %d",
+							 conf->gen.name, (int) conf->boot_val);
+				*conf->variable = conf->reset_val = conf->boot_val;
+				break;
+			}
+		case PGC_INT:
+			{
+				struct config_int *conf = (struct config_int *) gconf;
+
+				Assert(conf->boot_val >= conf->min);
+				Assert(conf->boot_val <= conf->max);
+				if (conf->assign_hook)
+					if (!(*conf->assign_hook) (conf->boot_val, true,
+											   PGC_S_DEFAULT))
+						elog(FATAL, "failed to initialize %s to %d",
+							 conf->gen.name, conf->boot_val);
+				*conf->variable = conf->reset_val = conf->boot_val;
+				break;
+			}
+		case PGC_REAL:
+			{
+				struct config_real *conf = (struct config_real *) gconf;
+
+				Assert(conf->boot_val >= conf->min);
+				Assert(conf->boot_val <= conf->max);
+				if (conf->assign_hook)
+					if (!(*conf->assign_hook) (conf->boot_val, true,
+											   PGC_S_DEFAULT))
+						elog(FATAL, "failed to initialize %s to %g",
+							 conf->gen.name, conf->boot_val);
+				*conf->variable = conf->reset_val = conf->boot_val;
+				break;
+			}
+		case PGC_STRING:
+			{
+				struct config_string *conf = (struct config_string *) gconf;
+				char	   *str;
+
+				*conf->variable = NULL;
+				conf->reset_val = NULL;
+
+				if (conf->boot_val == NULL)
+				{
+					/* leave the value NULL, do not call assign hook */
+					break;
+				}
+
+				str = guc_strdup(FATAL, conf->boot_val);
+				conf->reset_val = str;
+
+				if (conf->assign_hook)
+				{
+					const char *newstr;
+
+					newstr = (*conf->assign_hook) (str, true,
+												   PGC_S_DEFAULT);
+					if (newstr == NULL)
+					{
+						elog(FATAL, "failed to initialize %s to \"%s\"",
+							 conf->gen.name, str);
+					}
+					else if (newstr != str)
+					{
+						free(str);
+
+						/*
+						 * See notes in set_config_option about casting
+						 */
+						str = (char *) newstr;
+						conf->reset_val = str;
+					}
+				}
+				*conf->variable = str;
+				break;
+			}
+		case PGC_ENUM:
+			{
+				struct config_enum *conf = (struct config_enum *) gconf;
+
+				if (conf->assign_hook)
+					if (!(*conf->assign_hook) (conf->boot_val, true,
+											   PGC_S_DEFAULT))
+						elog(FATAL, "failed to initialize %s to %s",
+							 conf->gen.name, 
+							 config_enum_lookup_by_value(conf, conf->boot_val));
+				*conf->variable = conf->reset_val = conf->boot_val;
+				break;
+			}
+	}
+}
 
 /*
  * Select the configuration files and data directory to be used, and
@@ -5656,6 +5661,7 @@ define_custom_variable(struct config_generic * variable)
 	if (res == NULL)
 	{
 		/* No placeholder to replace, so just add it */
+		initialize_option(variable);
 		add_guc_variable(variable, ERROR);
 		return;
 	}
@@ -5690,6 +5696,8 @@ define_custom_variable(struct config_generic * variable)
 		set_config_option(name, value,
 						  pHolder->gen.context, pHolder->gen.source,
 						  GUC_ACTION_SET, true);
+	else
+		initialize_option(variable);
 
 	/*
 	 * Free up as much as we conveniently can of the placeholder structure
@@ -5823,6 +5831,29 @@ DefineCustomEnumVariable(const char *name,
 	define_custom_variable(&var->gen);
 }
 
+static const int config_varsize[] =
+{
+	sizeof(struct config_bool),
+	sizeof(struct config_int),
+	sizeof(struct config_real),
+	sizeof(struct config_string),
+	sizeof(struct config_enum),
+};
+
+void
+DefineCustomVariable(enum config_type type, const void *variable)
+{
+	int		size = config_varsize[type];
+	const struct config_generic	   *var = variable;
+	struct config_generic		   *gen;
+
+	gen = (struct config_generic *) guc_malloc(ERROR, size);
+	memcpy(gen, var, size);
+	gen->name = guc_strdup(ERROR, var->name);
+	gen->vartype = type;
+	define_custom_variable(gen);
+}
+
 void
 EmitWarningsOnPlaceholders(const char *className)
 {
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 5b808d5..cf07915 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -41,4 +41,7 @@ extern void ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
 extern void ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
 			   ExplainStmt *stmt, TupOutputState *tstate);
 
+extern void ExplainOneResult(StringInfo str, QueryDesc *queryDesc,
+							 bool analyze, bool verbose);
+
 #endif   /* EXPLAIN_H */
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index 2e2c159..854721c 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -20,6 +20,8 @@
 #include "tcop/dest.h"
 
 
+extern PGDLLIMPORT bool force_instrument;
+
 /* ----------------
  *		query descriptor:
  *
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 8791660..3982a27 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -242,5 +242,6 @@ extern const char *config_enum_lookup_by_value(struct config_enum *record, int v
 extern bool config_enum_lookup_by_name(struct config_enum *record,
 									  const char *value, int *retval);
 
+extern void DefineCustomVariable(enum config_type type, const void *variable);
 
 #endif   /* GUC_TABLES_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