1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

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
This commit is contained in:
Tom Lane
2018-09-18 17:11:54 -04:00
parent db1071d4ee
commit d0cfc3d6a4
7 changed files with 170 additions and 29 deletions

View File

@ -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.
*/