From 10e30cc0627c3eb325b15033e36a66f22f5c2fbd Mon Sep 17 00:00:00 2001
From: Qingqing Zhou <qingqing@qq-1.(none)>
Date: Mon, 27 Jul 2015 04:57:56 -0700
Subject: [PATCH 2/9] local change

---
 src/backend/nodes/print.c             |   62 +++---
 src/backend/optimizer/path/allpaths.c |  334 +++++++++++++++++++++++++++------
 src/backend/optimizer/path/joinrels.c |    3 +
 src/backend/optimizer/plan/planner.c  |   11 +-
 src/backend/optimizer/util/pathnode.c |   52 ++++--
 src/backend/optimizer/util/relnode.c  |    6 +
 src/backend/utils/misc/guc.c          |   30 +++
 src/include/nodes/print.h             |    6 +-
 src/include/nodes/relation.h          |    8 +-
 src/include/optimizer/pathnode.h      |    8 +
 src/include/optimizer/paths.h         |    3 -
 src/include/optimizer/planmain.h      |    8 +
 12 files changed, 414 insertions(+), 117 deletions(-)

diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index 72b2912..2d31f4a 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -305,11 +305,11 @@ print_rt(const List *rtable)
  *	  print an expression
  */
 void
-print_expr(const Node *expr, const List *rtable)
+print_expr(StringInfo str, const Node *expr, const List *rtable)
 {
 	if (expr == NULL)
 	{
-		printf("<>");
+		appendStringInfoString(str, "<>");
 		return;
 	}
 
@@ -345,7 +345,7 @@ print_expr(const Node *expr, const List *rtable)
 				}
 				break;
 		}
-		printf("%s.%s", relname, attname);
+		appendStringInfo(str, "%s.%s", relname, attname);
 	}
 	else if (IsA(expr, Const))
 	{
@@ -356,7 +356,7 @@ print_expr(const Node *expr, const List *rtable)
 
 		if (c->constisnull)
 		{
-			printf("NULL");
+			appendStringInfoString(str, "NULL");
 			return;
 		}
 
@@ -364,7 +364,7 @@ print_expr(const Node *expr, const List *rtable)
 						  &typoutput, &typIsVarlena);
 
 		outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
-		printf("%s", outputstr);
+		appendStringInfoString(str, outputstr);
 		pfree(outputstr);
 	}
 	else if (IsA(expr, OpExpr))
@@ -375,15 +375,15 @@ print_expr(const Node *expr, const List *rtable)
 		opname = get_opname(e->opno);
 		if (list_length(e->args) > 1)
 		{
-			print_expr(get_leftop((const Expr *) e), rtable);
-			printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
-			print_expr(get_rightop((const Expr *) e), rtable);
+			print_expr(str, get_leftop((const Expr *) e), rtable);
+			appendStringInfo(str, " %s ", ((opname != NULL) ? opname : "(invalid operator)"));
+			print_expr(str, get_rightop((const Expr *) e), rtable);
 		}
 		else
 		{
 			/* we print prefix and postfix ops the same... */
-			printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
-			print_expr(get_leftop((const Expr *) e), rtable);
+			appendStringInfo(str, "%s ", ((opname != NULL) ? opname : "(invalid operator)"));
+			print_expr(str, get_leftop((const Expr *) e), rtable);
 		}
 	}
 	else if (IsA(expr, FuncExpr))
@@ -393,17 +393,17 @@ print_expr(const Node *expr, const List *rtable)
 		ListCell   *l;
 
 		funcname = get_func_name(e->funcid);
-		printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
+		appendStringInfo(str, "%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
 		foreach(l, e->args)
 		{
-			print_expr(lfirst(l), rtable);
+			print_expr(str, lfirst(l), rtable);
 			if (lnext(l))
-				printf(",");
+				appendStringInfoChar(str, ',');
 		}
-		printf(")");
+		appendStringInfoChar(str, ')');
 	}
 	else
-		printf("unknown expr");
+		appendStringInfoString(str, "unknown expr");
 }
 
 /*
@@ -411,11 +411,11 @@ print_expr(const Node *expr, const List *rtable)
  *	  pathkeys list of PathKeys
  */
 void
-print_pathkeys(const List *pathkeys, const List *rtable)
+print_pathkeys(StringInfo str, const List *pathkeys, const List *rtable)
 {
 	const ListCell *i;
 
-	printf("(");
+	appendStringInfoChar(str, '(');
 	foreach(i, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(i);
@@ -428,7 +428,7 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 		while (eclass->ec_merged)
 			eclass = eclass->ec_merged;
 
-		printf("(");
+		appendStringInfoChar(str, '(');
 		foreach(k, eclass->ec_members)
 		{
 			EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
@@ -436,14 +436,14 @@ print_pathkeys(const List *pathkeys, const List *rtable)
 			if (first)
 				first = false;
 			else
-				printf(", ");
-			print_expr((Node *) mem->em_expr, rtable);
+				appendStringInfoString(str, ", ");
+			print_expr(str, (Node *) mem->em_expr, rtable);
 		}
-		printf(")");
+		appendStringInfoChar(str, ')');
 		if (lnext(i))
-			printf(", ");
+			appendStringInfoString(str, ", ");
 	}
-	printf(")\n");
+	appendStringInfoString(str, ")\n");
 }
 
 /*
@@ -451,25 +451,25 @@ print_pathkeys(const List *pathkeys, const List *rtable)
  *	  print targetlist in a more legible way.
  */
 void
-print_tl(const List *tlist, const List *rtable)
+print_tl(StringInfo str, const List *tlist, const List *rtable)
 {
 	const ListCell *tl;
 
-	printf("(\n");
+	appendStringInfoString(str, "(\n");
 	foreach(tl, tlist)
 	{
 		TargetEntry *tle = (TargetEntry *) lfirst(tl);
 
-		printf("\t%d %s\t", tle->resno,
+		appendStringInfo(str, "\t%d %s\t", tle->resno,
 			   tle->resname ? tle->resname : "<null>");
 		if (tle->ressortgroupref != 0)
-			printf("(%u):\t", tle->ressortgroupref);
+			appendStringInfo(str, "(%u):\t", tle->ressortgroupref);
 		else
-			printf("    :\t");
-		print_expr((Node *) tle->expr, rtable);
-		printf("\n");
+			appendStringInfoString(str, "    :\t");
+		print_expr(str, (Node *) tle->expr, rtable);
+		appendStringInfoChar(str, '\n');
 	}
-	printf(")\n");
+	appendStringInfoString(str, ")\n");
 }
 
 /*
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 888eeac..5db48f4 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include <math.h>
+#include <unistd.h>
 
 #include "access/sysattr.h"
 #include "catalog/pg_class.h"
@@ -23,9 +24,7 @@
 #include "foreign/fdwapi.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
-#ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
-#endif
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/geqo.h"
@@ -33,6 +32,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/planner.h"
+#include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/var.h"
@@ -40,6 +40,7 @@
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
+#include "storage/fd.h"
 
 
 /* results of subquery_is_pushdown_safe */
@@ -120,6 +121,17 @@ static void recurse_push_qual(Node *setOp, Query *topquery,
 				  RangeTblEntry *rte, Index rti, Node *qual);
 static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
 
+/* debug support */
+static int debug_planner_fopen(char *filename);
+static void debug_planner_fappend(int fd, StringInfo str);
+static void debug_planner_fclose(int fd);
+static void print_relids(StringInfo str, Relids relids);
+static void print_restrictclauses(StringInfo str, PlannerInfo *root, List *clauses);
+static void print_path(StringInfo str, PlannerInfo *root, Path *path, int indent);
+static void print_rel(StringInfo str, PlannerInfo *root, RelOptInfo *rel);
+static void print_path_csv(StringInfo str, PlannerInfo *root, 
+					RelOptInfo *rel, Path *path, Path *replacedby, PathCostComparison costcmp);
+static void print_searchspace_csv(PlannerInfo *root, RelOptInfo *rel);
 
 /*
  * make_one_rel
@@ -430,9 +442,8 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	/* Now find the cheapest of the paths for this rel */
 	set_cheapest(rel);
 
-#ifdef OPTIMIZER_DEBUG
-	debug_print_rel(root, rel);
-#endif
+	if (debug_planner > DEBUG_PLANNER_OFF)
+		debug_print_rel(root, rel);
 }
 
 /*
@@ -1182,6 +1193,9 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
 
 	/* Discard any pre-existing paths; no further need for them */
 	rel->pathlist = NIL;
+	rel->pathlist_removed = NIL;
+	rel->pathlist_reason = NIL;
+	rel->pathlist_replaceby = NIL;
 
 	add_path(rel, (Path *) create_append_path(rel, NIL, NULL));
 
@@ -1745,9 +1759,8 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
 			/* Find and save the cheapest paths for this rel */
 			set_cheapest(rel);
 
-#ifdef OPTIMIZER_DEBUG
-			debug_print_rel(root, rel);
-#endif
+			if (debug_planner > DEBUG_PLANNER_OFF)
+				debug_print_rel(root, rel);
 		}
 	}
 
@@ -2405,27 +2418,95 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel)
 /*****************************************************************************
  *			DEBUG SUPPORT
  *****************************************************************************/
+#define DEBUG_PLAN_RELOPT_FILE			"debug_planner_relopt.csv"	/* file to save RelOptInfos */
+#define DEBUG_PLAN_PATH_FILE			"debug_planner_paths.csv"	/* file to save Paths */
+	
+static char* debug_planner_filename = NULL;
 
-#ifdef OPTIMIZER_DEBUG
 
+/*
+ * debug_planner_fopen
+ *	  Open file for planner debug information output
+ */
+static int
+debug_planner_fopen(char *filename)
+{
+	int 	fd;
+
+	debug_planner_filename = filename;
+
+	/* the file is used as a log style, so open it with append and write only */
+	fd = BasicOpenFile(filename,
+						  O_CREAT | O_APPEND | O_WRONLY,
+						  S_IRUSR | S_IWUSR);
+	if (fd < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not open planner debug file \"%s\": %m",
+						filename)));
+
+	return fd;
+}
+
+/*
+ * debug_planner_fappend
+ *	  Append string to the planer debug file
+ */
+static void 
+debug_planner_fappend(int fd, StringInfo str)
+{
+	/* no lseek to the end is needed as the file is in append mode */
+	errno = 0;
+	if (write(fd, str->data, str->len) != str->len)
+	{
+		/* if write didn't set errno, assume problem is no disk space */
+		if (errno == 0)
+			errno = ENOSPC;
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to planner debug file \"%s\": %m", debug_planner_filename)));
+	}
+}
+
+/*
+ * debug_planner_fclose
+ *	  Close planner debug file
+ */
+static void 
+debug_planner_fclose(int fd)
+{
+	if (close(fd) != 0)
+		ereport(LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not clsoe planner debug file \"%s\": %m", debug_planner_filename)));
+}
+
+/*
+ * print_relids
+ *	  Print Relids to the target buffer
+ */
 static void
-print_relids(Relids relids)
+print_relids(StringInfo str, Relids relids)
 {
 	int			x;
 	bool		first = true;
-
+	
 	x = -1;
 	while ((x = bms_next_member(relids, x)) >= 0)
 	{
 		if (!first)
-			printf(" ");
-		printf("%d", x);
+			appendStringInfoSpaces(str, 1);
+		appendStringInfo(str, "%d", x);
 		first = false;
 	}
 }
 
+/*
+ * print_restrictclauses
+ *	  Print RestrictInfo list to the target buffer
+ */
 static void
-print_restrictclauses(PlannerInfo *root, List *clauses)
+print_restrictclauses(StringInfo str, PlannerInfo *root, List *clauses)
 {
 	ListCell   *l;
 
@@ -2433,14 +2514,18 @@ print_restrictclauses(PlannerInfo *root, List *clauses)
 	{
 		RestrictInfo *c = lfirst(l);
 
-		print_expr((Node *) c->clause, root->parse->rtable);
+		print_expr(str, (Node *) c->clause, root->parse->rtable);
 		if (lnext(l))
-			printf(", ");
+			appendStringInfoString(str, ", ");
 	}
 }
 
+/*
+ * print_path
+ *	  Print a Path to the target buffer
+ */
 static void
-print_path(PlannerInfo *root, Path *path, int indent)
+print_path(StringInfo str, PlannerInfo *root, Path *path, int indent)
 {
 	const char *ptype;
 	bool		join = false;
@@ -2505,23 +2590,23 @@ print_path(PlannerInfo *root, Path *path, int indent)
 	}
 
 	for (i = 0; i < indent; i++)
-		printf("\t");
-	printf("%s", ptype);
+		appendStringInfoChar(str, '\t');
+	appendStringInfoString(str, ptype);
 
 	if (path->parent)
 	{
-		printf("(");
-		print_relids(path->parent->relids);
-		printf(") rows=%.0f", path->parent->rows);
+		appendStringInfoChar(str, '(');
+		print_relids(str, path->parent->relids);
+		appendStringInfo(str, ") rows=%.0f", path->parent->rows);
 	}
-	printf(" cost=%.2f..%.2f\n", path->startup_cost, path->total_cost);
+	appendStringInfo(str, " cost=%.2f..%.2f\n", path->startup_cost, path->total_cost);
 
 	if (path->pathkeys)
 	{
 		for (i = 0; i < indent; i++)
-			printf("\t");
-		printf("  pathkeys: ");
-		print_pathkeys(path->pathkeys, root->parse->rtable);
+			appendStringInfoChar(str, '\t');
+		appendStringInfoString(str, "  pathkeys: ");
+		print_pathkeys(str, path->pathkeys, root->parse->rtable);
 	}
 
 	if (join)
@@ -2529,69 +2614,206 @@ print_path(PlannerInfo *root, Path *path, int indent)
 		JoinPath   *jp = (JoinPath *) path;
 
 		for (i = 0; i < indent; i++)
-			printf("\t");
-		printf("  clauses: ");
-		print_restrictclauses(root, jp->joinrestrictinfo);
-		printf("\n");
+			appendStringInfoChar(str, '\t');
+		appendStringInfoString(str, "  clauses: ");
+		print_restrictclauses(str, root, jp->joinrestrictinfo);
+		appendStringInfoChar(str, '\n');
 
 		if (IsA(path, MergePath))
 		{
 			MergePath  *mp = (MergePath *) path;
 
 			for (i = 0; i < indent; i++)
-				printf("\t");
-			printf("  sortouter=%d sortinner=%d materializeinner=%d\n",
+				appendStringInfoChar(str, '\t');
+			appendStringInfo(str, "  sortouter=%d sortinner=%d materializeinner=%d\n",
 				   ((mp->outersortkeys) ? 1 : 0),
 				   ((mp->innersortkeys) ? 1 : 0),
 				   ((mp->materialize_inner) ? 1 : 0));
 		}
 
-		print_path(root, jp->outerjoinpath, indent + 1);
-		print_path(root, jp->innerjoinpath, indent + 1);
+		print_path(str, root, jp->outerjoinpath, indent + 1);
+		print_path(str, root, jp->innerjoinpath, indent + 1);
 	}
 
 	if (subpath)
-		print_path(root, subpath, indent + 1);
+		print_path(str, root, subpath, indent + 1);
 }
 
-void
-debug_print_rel(PlannerInfo *root, RelOptInfo *rel)
+/*
+ * print_path
+ *	  Print a RelOptInfo to the target buffer
+ */
+static void
+print_rel(StringInfo str, PlannerInfo *root, RelOptInfo *rel)
 {
-	ListCell   *l;
+	ListCell   		*l;
 
-	printf("RELOPTINFO (");
-	print_relids(rel->relids);
-	printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
+	appendStringInfoString(str, "RELOPTINFO (");
+	print_relids(str, rel->relids);
+	appendStringInfo(str, "): rows=%.0f width=%d\n", rel->rows, rel->width);
 
 	if (rel->baserestrictinfo)
 	{
-		printf("\tbaserestrictinfo: ");
-		print_restrictclauses(root, rel->baserestrictinfo);
-		printf("\n");
+		appendStringInfoString(str, "\tbaserestrictinfo: ");
+		print_restrictclauses(str, root, rel->baserestrictinfo);
+		appendStringInfoChar(str, '\n');
 	}
 
 	if (rel->joininfo)
 	{
-		printf("\tjoininfo: ");
-		print_restrictclauses(root, rel->joininfo);
-		printf("\n");
+		appendStringInfoString(str, "\tjoininfo: ");
+		print_restrictclauses(str, root, rel->joininfo);
+		appendStringInfoChar(str, '\n');
 	}
 
-	printf("\tpath list:\n");
+	appendStringInfoString(str, "\tpath list:\n");
 	foreach(l, rel->pathlist)
-		print_path(root, lfirst(l), 1);
+		print_path(str, root, lfirst(l), 1);
 	if (rel->cheapest_startup_path)
 	{
-		printf("\n\tcheapest startup path:\n");
-		print_path(root, rel->cheapest_startup_path, 1);
+		appendStringInfoString(str, "\n\tcheapest startup path:\n");
+		print_path(str, root, rel->cheapest_startup_path, 1);
 	}
 	if (rel->cheapest_total_path)
 	{
-		printf("\n\tcheapest total path:\n");
-		print_path(root, rel->cheapest_total_path, 1);
+		appendStringInfoString(str, "\n\tcheapest total path:\n");
+		print_path(str, root, rel->cheapest_total_path, 1);
 	}
-	printf("\n");
-	fflush(stdout);
+	appendStringInfoChar(str, '\n');
 }
 
-#endif   /* OPTIMIZER_DEBUG */
+/*
+ * print_path_csv
+ *	  Print a Path (and related) to the target buffer with a CSV row format
+ */
+static void
+print_path_csv(StringInfo str, PlannerInfo *root, 
+					RelOptInfo *rel, Path *path, 
+					Path *replacedby, PathCostComparison costcmp)
+{
+	/* path better matches the rel */
+	Assert (path->parent == rel);
+
+	/*
+	  * column 1: pointer to parent RelOptInfo, so they can join
+	  * column 2: pointer to current path, which is used as a key
+	  * column 3: replace by which path if any
+	  * column 4: reason of replace: cost comparison result 
+	  * column 5: startup cost of the path
+	  * column 6: total cost of the path
+	  */
+	if (replacedby == NULL)
+	{
+		ListCell	   *lc;
+
+		appendStringInfo(str, "%p,%p,,,%.2f,%.2f,", 
+				rel, path, path->startup_cost, path->total_cost);
+
+		/* column 7: is it the best of what kind of path: it can be multiple */
+		if (path == rel->cheapest_total_path)
+			appendStringInfoString(str, "+total");
+		if (path == rel->cheapest_startup_path)
+			appendStringInfoString(str, "+startup");
+		if (path == rel->cheapest_unique_path)
+			appendStringInfoString(str, "+unique");
+		foreach(lc, rel->cheapest_parameterized_paths)
+		{
+			if (path == (Path *)lfirst(lc))
+				appendStringInfoString(str, "+param");
+		}
+	}
+	else
+		appendStringInfo(str, "%p,%p,%p,%d,%.2f,%.2f,", 
+				rel, path, replacedby, costcmp, path->startup_cost, path->total_cost);
+
+	/* column 8,9: inner and outer path, for join node only */
+	if (IsA(path, NestPath) || IsA(path, MergePath) || IsA(path, HashPath))
+	{
+		JoinPath	*jpath = (JoinPath *)path;
+		
+		appendStringInfo(str, ",%p,%p", 
+							jpath->innerjoinpath, jpath->outerjoinpath);
+	}
+	else
+		appendStringInfoString(str, ",,");
+	
+	/* column 10: actual path content */
+	appendStringInfoString(str, ",\"");
+	print_path(str, root, path, 0);
+	appendStringInfoString(str, "\"\n");
+}
+
+/*
+ * print_searchspace_csv
+ *	  Print current search space to CSV files
+ */
+static void
+print_searchspace_csv(PlannerInfo *root, RelOptInfo *rel)
+{
+	StringInfoData	strdata, *str;
+	int				fd;
+	ListCell   	   *lc1, *lc2, *lc3;
+		
+	initStringInfo(&strdata);
+	str = &strdata;
+
+	/* 
+	  * Print all RelOptInfos to one file
+	  */
+	fd = debug_planner_fopen(DEBUG_PLAN_RELOPT_FILE);
+	/* column 1: pointer to RelOptInfo, which is used as a key */
+	appendStringInfo(str, "%p", rel);
+	/* column 2: actual RelOptInfo content */
+	appendStringInfoString(str, ",\"");
+	appendStringInfo(str, "RELOPTINFO (");
+	print_relids(str, rel->relids);
+	appendStringInfo(str, "): rows=%.0f width=%d", rel->rows, rel->width);
+	appendStringInfoString(str, "\"\n");
+	debug_planner_fappend(fd, str);
+	debug_planner_fclose(fd);
+	
+	/* 
+	  * Print all kept paths in the final list to another file
+	  */
+	fd = debug_planner_fopen(DEBUG_PLAN_PATH_FILE);
+	foreach(lc1, rel->pathlist)
+	{
+		Path 	*path = lfirst(lc1);
+
+		resetStringInfo(str);
+		print_path_csv(str, root, rel, path, NULL, COSTS_EQUAL /* not used */);
+		debug_planner_fappend(fd, str);
+	}
+	forthree(lc1, rel->pathlist_removed, 
+				lc2, rel->pathlist_replaceby, lc3, rel->pathlist_reason)
+	{
+		Path 	*removed = lfirst(lc1);
+		Path 	*replaceby = lfirst(lc2);
+		PathCostComparison	costcmp = lfirst_int(lc3);
+
+		resetStringInfo(str);
+		print_path_csv(str, root, rel, removed, replaceby, costcmp);
+		debug_planner_fappend(fd, str);
+	}
+	debug_planner_fclose(fd);
+	pfree(strdata.data);
+}
+
+/*
+ * debug_print_rel
+ *	  Print an RelOptInfo to console and dump search space.
+ */
+void
+debug_print_rel(PlannerInfo *root, RelOptInfo *rel)
+{
+	StringInfoData	str;
+
+	initStringInfo(&str);
+	print_rel(&str, root, rel);
+
+	printf("%s", str.data);
+	pfree(str.data);
+	fflush(stdout);
+
+	print_searchspace_csv(root, rel);
+}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index fe9fd57..4780c88 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1054,6 +1054,9 @@ mark_dummy_rel(RelOptInfo *rel)
 
 	/* Evict any previously chosen paths */
 	rel->pathlist = NIL;
+	rel->pathlist_removed = NIL;
+	rel->pathlist_reason = NIL;
+	rel->pathlist_replaceby = NIL;
 
 	/* Set up the dummy path */
 	add_path(rel, (Path *) create_append_path(rel, NIL, NULL));
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a6ce96e..96284a5 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -26,9 +26,7 @@
 #include "lib/bipartite_match.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
-#ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
-#endif
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
@@ -728,10 +726,11 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
 	{
 		expr = (Node *) canonicalize_qual((Expr *) expr);
 
-#ifdef OPTIMIZER_DEBUG
-		printf("After canonicalize_qual()\n");
-		pprint(expr);
-#endif
+		if (debug_planner > DEBUG_PLANNER_OFF)
+		{
+			printf("After canonicalize_qual()\n");
+			pprint(expr);
+		}
 	}
 
 	/* Expand SubLinks to SubPlans */
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index f7f33bb..852ca17 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -30,14 +30,6 @@
 #include "utils/selfuncs.h"
 
 
-typedef enum
-{
-	COSTS_EQUAL,				/* path costs are fuzzily equal */
-	COSTS_BETTER1,				/* first path is cheaper than second */
-	COSTS_BETTER2,				/* second path is cheaper than first */
-	COSTS_DIFFERENT				/* neither path dominates the other on cost */
-} PathCostComparison;
-
 /*
  * STD_FUZZ_FACTOR is the normal fuzz factor for compare_path_costs_fuzzily.
  * XXX is it worth making this user-controllable?  It provides a tradeoff
@@ -405,6 +397,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 	ListCell   *p1;
 	ListCell   *p1_prev;
 	ListCell   *p1_next;
+	PathCostComparison costcmp;
 
 	/*
 	 * This is a convenient place to check for query cancel --- no part of the
@@ -428,7 +421,6 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 	{
 		Path	   *old_path = (Path *) lfirst(p1);
 		bool		remove_old = false; /* unless new proves superior */
-		PathCostComparison costcmp;
 		PathKeysComparison keyscmp;
 		BMS_Comparison outercmp;
 
@@ -558,14 +550,27 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 		 */
 		if (remove_old)
 		{
+			/*
+			 * Delete the data pointed-to by the deleted cell, if possible. 
+			 */
 			parent_rel->pathlist = list_delete_cell(parent_rel->pathlist,
 													p1, p1_prev);
 
-			/*
-			 * Delete the data pointed-to by the deleted cell, if possible
-			 */
-			if (!IsA(old_path, IndexPath))
-				pfree(old_path);
+			if (debug_planner > DEBUG_PLANNER_OFF)
+			{
+				/* keep removed path and reason for debug */
+				parent_rel->pathlist_removed = lcons(old_path, 
+											parent_rel->pathlist_removed);
+				parent_rel->pathlist_reason = lcons_int(costcmp, 
+											parent_rel->pathlist_reason);
+				parent_rel->pathlist_replaceby = lcons(new_path, 
+											parent_rel->pathlist_replaceby);
+			}
+			else
+			{
+				if (!IsA(old_path, IndexPath))
+					pfree(old_path);
+			}
 			/* p1_prev does not advance */
 		}
 		else
@@ -596,9 +601,22 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 	}
 	else
 	{
-		/* Reject and recycle the new path */
-		if (!IsA(new_path, IndexPath))
-			pfree(new_path);
+		if (debug_planner > DEBUG_PLANNER_OFF)
+		{
+			/* keep removed path and reason for debug */
+			parent_rel->pathlist_removed = lcons(new_path, 
+										parent_rel->pathlist_removed);
+			parent_rel->pathlist_reason = lcons_int(costcmp, 
+										parent_rel->pathlist_reason);
+			parent_rel->pathlist_replaceby = lcons(new_path, 
+										parent_rel->pathlist_replaceby);
+		}
+		else
+		{
+			/* Reject and recycle the new path */
+			if (!IsA(new_path, IndexPath))
+				pfree(new_path);
+		}
 	}
 }
 
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index be2ef3b..8731392 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -104,6 +104,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	rel->consider_param_startup = false;		/* might get changed later */
 	rel->reltargetlist = NIL;
 	rel->pathlist = NIL;
+	rel->pathlist_removed = NIL;
+	rel->pathlist_reason = NIL;
+	rel->pathlist_replaceby = NIL;
 	rel->ppilist = NIL;
 	rel->cheapest_startup_path = NULL;
 	rel->cheapest_total_path = NULL;
@@ -365,6 +368,9 @@ build_join_rel(PlannerInfo *root,
 	joinrel->consider_param_startup = false;
 	joinrel->reltargetlist = NIL;
 	joinrel->pathlist = NIL;
+	joinrel->pathlist_removed = NIL;
+	joinrel->pathlist_reason = NIL;
+	joinrel->pathlist_replaceby = NIL;
 	joinrel->ppilist = NIL;
 	joinrel->cheapest_startup_path = NULL;
 	joinrel->cheapest_total_path = NULL;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1bed525..7cdf1c7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -84,6 +84,7 @@
 #include "utils/tzparser.h"
 #include "utils/xml.h"
 
+
 #ifndef PG_KRB_SRVTAB
 #define PG_KRB_SRVTAB ""
 #endif
@@ -397,6 +398,23 @@ static const struct config_enum_entry row_security_options[] = {
 };
 
 /*
+  * Although only "on", "off" are documented, we accept all the likely
+  * variants of "on" and "off".
+  */
+static const struct config_enum_entry debug_planner_options[] = {
+	{"on", DEBUG_PLANNER_ON, false},
+	{"off", DEBUG_PLANNER_OFF, false},
+	{"true", DEBUG_PLANNER_ON, true},
+	{"false", DEBUG_PLANNER_OFF, true},
+	{"yes", DEBUG_PLANNER_ON, true},
+	{"no", DEBUG_PLANNER_OFF, true},
+	{"1", DEBUG_PLANNER_ON, true},
+	{"0", DEBUG_PLANNER_OFF, true},
+	{NULL, 0, false}
+};
+
+
+/*
  * Options for enum values stored in other modules
  */
 extern const struct config_enum_entry wal_level_options[];
@@ -412,6 +430,7 @@ bool		Debug_print_plan = false;
 bool		Debug_print_parse = false;
 bool		Debug_print_rewritten = false;
 bool		Debug_pretty_print = true;
+int			debug_planner;
 
 bool		log_parser_stats = false;
 bool		log_planner_stats = false;
@@ -3652,6 +3671,17 @@ static struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"debug_planner", PGC_USERSET, QUERY_TUNING,
+			gettext_noop("Enable planner debug."),
+			gettext_noop("When enabled, this will dump planner searchspace to CSV file. "
+						 "And this may increase query planner memory usage."),
+		},
+		&debug_planner,
+		DEBUG_PLANNER_OFF, debug_planner_options,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index 5885916..356eb53 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -26,9 +26,9 @@ extern void elog_node_display(int lev, const char *title,
 extern char *format_node_dump(const char *dump);
 extern char *pretty_format_node_dump(const char *dump);
 extern void print_rt(const List *rtable);
-extern void print_expr(const Node *expr, const List *rtable);
-extern void print_pathkeys(const List *pathkeys, const List *rtable);
-extern void print_tl(const List *tlist, const List *rtable);
+extern void print_expr(StringInfo str, const Node *expr, const List *rtable);
+extern void print_pathkeys(StringInfo str, const List *pathkeys, const List *rtable);
+extern void print_tl(StringInfo str, const List *tlist, const List *rtable);
 extern void print_slot(TupleTableSlot *slot);
 
 #endif   /* PRINT_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index cb916ea..83a16c0 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -442,7 +442,13 @@ typedef struct RelOptInfo
 
 	/* materialization information */
 	List	   *reltargetlist;	/* Vars to be output by scan of relation */
-	List	   *pathlist;		/* Path structures */
+	List	   *pathlist;		/* Path structures that kept */
+
+	/* debug only information: the list all have the same length */
+	List	   *pathlist_removed;	/* Path that have been removed */
+	List	   *pathlist_reason;	/* Reason the path get removed */
+	List	   *pathlist_replaceby;	/* Path that replaces the removed */ 
+	
 	List	   *ppilist;		/* ParamPathInfos used in pathlist */
 	struct Path *cheapest_startup_path;
 	struct Path *cheapest_total_path;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 161644c..6a1e5c3 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -16,6 +16,14 @@
 
 #include "nodes/relation.h"
 
+/* expose PathCostComparison */
+typedef enum
+{
+	COSTS_EQUAL,				/* path costs are fuzzily equal */
+	COSTS_BETTER1,				/* first path is cheaper than second */
+	COSTS_BETTER2,				/* second path is cheaper than first */
+	COSTS_DIFFERENT				/* neither path dominates the other on cost */
+} PathCostComparison;
 
 /*
  * prototypes for pathnode.c
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 3e2378a..e995408 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -49,10 +49,7 @@ extern PGDLLIMPORT join_search_hook_type join_search_hook;
 extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
 extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
 					 List *initial_rels);
-
-#ifdef OPTIMIZER_DEBUG
 extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
-#endif
 
 /*
  * indxpath.c
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 52b077a..0cf1611 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -20,6 +20,14 @@
 /* GUC parameters */
 #define DEFAULT_CURSOR_TUPLE_FRACTION 0.1
 extern double cursor_tuple_fraction;
+extern int	debug_planner;
+
+/* Possible values for debug_planner GUC */
+typedef enum DebugPlannerConfigType
+{
+	DEBUG_PLANNER_OFF,			/* planner debug is off */
+	DEBUG_PLANNER_ON			/* planner debug is on */
+}	DebugPlannerConfigType;
 
 /* query_planner callback to compute query_pathkeys */
 typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);
-- 
1.7.1

