From 10e30cc0627c3eb325b15033e36a66f22f5c2fbd Mon Sep 17 00:00:00 2001 From: Qingqing Zhou 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 : ""); 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 +#include #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