diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2018-09-18 17:11:54 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2018-09-18 17:11:54 -0400 |
commit | d0cfc3d6a44af1756ca5be8cb2414da7b8bf20d5 (patch) | |
tree | 5fdb37364f283c7965c53512d2bd58e6d6699cca /src/backend/tcop/postgres.c | |
parent | db1071d4ee9f0e348ab646e7c13184d480d40516 (diff) | |
download | postgresql-d0cfc3d6a44af1756ca5be8cb2414da7b8bf20d5.tar.gz postgresql-d0cfc3d6a44af1756ca5be8cb2414da7b8bf20d5.zip |
Add a debugging option to stress-test outfuncs.c and readfuncs.c.
In the normal course of operation, query trees will be serialized only if
they are stored as views or rules; and plan trees will be serialized only
if they get passed to parallel-query workers. This leaves an awful lot of
opportunity for bugs/oversights to not get detected, as indeed we've just
been reminded of the hard way.
To improve matters, this patch adds a new compile option
WRITE_READ_PARSE_PLAN_TREES, which is modeled on the longstanding option
COPY_PARSE_PLAN_TREES; but instead of passing all parse and plan trees
through copyObject, it passes them through nodeToString + stringToNode.
Enabling this option in a buildfarm animal or two will catch problems
at least for cases that are exercised by the regression tests.
A small problem with this idea is that readfuncs.c historically has
discarded location fields, on the reasonable grounds that parse
locations in a retrieved view are not relevant to the current query.
But doing that in WRITE_READ_PARSE_PLAN_TREES breaks pg_stat_statements,
and it could cause problems for future improvements that might try to
report error locations at runtime. To fix that, provide a variant
behavior in readfuncs.c that makes it restore location fields when
told to.
In passing, const-ify the string arguments of stringToNode and its
subsidiary functions, just because it annoyed me that they weren't
const already.
Discussion: https://postgr.es/m/17114.1537138992@sss.pgh.pa.us
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r-- | src/backend/tcop/postgres.c | 74 |
1 files changed, 72 insertions, 2 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 7a9ada2c719..e4c6e3d406e 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -633,6 +633,12 @@ pg_parse_query(const char *query_string) } #endif + /* + * Currently, outfuncs/readfuncs support is missing for many raw parse + * tree nodes, so we don't try to implement WRITE_READ_PARSE_PLAN_TREES + * here. + */ + TRACE_POSTGRESQL_QUERY_PARSE_DONE(query_string); return raw_parsetree_list; @@ -763,7 +769,7 @@ pg_rewrite_query(Query *query) ShowUsage("REWRITER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES - /* Optional debugging check: pass querytree output through copyObject() */ + /* Optional debugging check: pass querytree through copyObject() */ { List *new_list; @@ -776,6 +782,46 @@ pg_rewrite_query(Query *query) } #endif +#ifdef WRITE_READ_PARSE_PLAN_TREES + /* Optional debugging check: pass querytree through outfuncs/readfuncs */ + { + List *new_list = NIL; + ListCell *lc; + + /* + * We currently lack outfuncs/readfuncs support for most utility + * statement types, so only attempt to write/read non-utility queries. + */ + foreach(lc, querytree_list) + { + Query *query = castNode(Query, lfirst(lc)); + + if (query->commandType != CMD_UTILITY) + { + char *str = nodeToString(query); + Query *new_query = stringToNodeWithLocations(str); + + /* + * queryId is not saved in stored rules, but we must preserve + * it here to avoid breaking pg_stat_statements. + */ + new_query->queryId = query->queryId; + + new_list = lappend(new_list, new_query); + pfree(str); + } + else + new_list = lappend(new_list, query); + } + + /* This checks both outfuncs/readfuncs and the equal() routines... */ + if (!equal(new_list, querytree_list)) + elog(WARNING, "outfuncs/readfuncs failed to produce equal parse tree"); + else + querytree_list = new_list; + } +#endif + if (Debug_print_rewritten) elog_node_display(LOG, "rewritten parse tree", querytree_list, Debug_pretty_print); @@ -812,7 +858,7 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams) ShowUsage("PLANNER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES - /* Optional debugging check: pass plan output through copyObject() */ + /* Optional debugging check: pass plan tree through copyObject() */ { PlannedStmt *new_plan = copyObject(plan); @@ -830,6 +876,30 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams) } #endif +#ifdef WRITE_READ_PARSE_PLAN_TREES + /* Optional debugging check: pass plan tree through outfuncs/readfuncs */ + { + char *str; + PlannedStmt *new_plan; + + str = nodeToString(plan); + new_plan = stringToNodeWithLocations(str); + pfree(str); + + /* + * equal() currently does not have routines to compare Plan nodes, so + * don't try to test equality here. Perhaps fix someday? + */ +#ifdef NOT_USED + /* This checks both outfuncs/readfuncs and the equal() routines... */ + if (!equal(new_plan, plan)) + elog(WARNING, "outfuncs/readfuncs failed to produce an equal plan tree"); + else +#endif + plan = new_plan; + } +#endif + /* * Print plan if debugging. */ |