1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +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

@@ -28,18 +28,30 @@
/* Static state for pg_strtok */
static char *pg_strtok_ptr = NULL;
static const char *pg_strtok_ptr = NULL;
/* State flag that determines how readfuncs.c should treat location fields */
#ifdef WRITE_READ_PARSE_PLAN_TREES
bool restore_location_fields = false;
#endif
/*
* stringToNode -
* returns a Node with a given legal ASCII representation
* builds a Node tree from its string representation (assumed valid)
*
* restore_loc_fields instructs readfuncs.c whether to restore location
* fields rather than set them to -1. This is currently only supported
* in builds with the WRITE_READ_PARSE_PLAN_TREES debugging flag set.
*/
void *
stringToNode(char *str)
static void *
stringToNodeInternal(const char *str, bool restore_loc_fields)
{
char *save_strtok;
void *retval;
const char *save_strtok;
#ifdef WRITE_READ_PARSE_PLAN_TREES
bool save_restore_location_fields;
#endif
/*
* We save and restore the pre-existing state of pg_strtok. This makes the
@@ -51,13 +63,45 @@ stringToNode(char *str)
pg_strtok_ptr = str; /* point pg_strtok at the string to read */
/*
* If enabled, likewise save/restore the location field handling flag.
*/
#ifdef WRITE_READ_PARSE_PLAN_TREES
save_restore_location_fields = restore_location_fields;
restore_location_fields = restore_loc_fields;
#endif
retval = nodeRead(NULL, 0); /* do the reading */
pg_strtok_ptr = save_strtok;
#ifdef WRITE_READ_PARSE_PLAN_TREES
restore_location_fields = save_restore_location_fields;
#endif
return retval;
}
/*
* Externally visible entry points
*/
void *
stringToNode(const char *str)
{
return stringToNodeInternal(str, false);
}
#ifdef WRITE_READ_PARSE_PLAN_TREES
void *
stringToNodeWithLocations(const char *str)
{
return stringToNodeInternal(str, true);
}
#endif
/*****************************************************************************
*
* the lisp token parser
@@ -104,11 +148,11 @@ stringToNode(char *str)
* code should add backslashes to a string constant to ensure it is treated
* as a single token.
*/
char *
const char *
pg_strtok(int *length)
{
char *local_str; /* working pointer to string */
char *ret_str; /* start of token to return */
const char *local_str; /* working pointer to string */
const char *ret_str; /* start of token to return */
local_str = pg_strtok_ptr;
@@ -166,7 +210,7 @@ pg_strtok(int *length)
* any protective backslashes in the token are removed.
*/
char *
debackslash(char *token, int length)
debackslash(const char *token, int length)
{
char *result = palloc(length + 1);
char *ptr = result;
@@ -198,10 +242,10 @@ debackslash(char *token, int length)
* Assumption: the ascii representation is legal
*/
static NodeTag
nodeTokenType(char *token, int length)
nodeTokenType(const char *token, int length)
{
NodeTag retval;
char *numptr;
const char *numptr;
int numlen;
/*
@@ -269,7 +313,7 @@ nodeTokenType(char *token, int length)
* this should only be invoked from within a stringToNode operation).
*/
void *
nodeRead(char *token, int tok_len)
nodeRead(const char *token, int tok_len)
{
Node *result;
NodeTag type;

View File

@@ -17,10 +17,14 @@
* never read executor state trees, either.
*
* Parse location fields are written out by outfuncs.c, but only for
* possible debugging use. When reading a location field, we discard
* debugging use. When reading a location field, we normally discard
* the stored value and set the location field to -1 (ie, "unknown").
* This is because nodes coming from a stored rule should not be thought
* to have a known location in the current query's text.
* However, if restore_location_fields is true, we do restore location
* fields from the string. This is currently intended only for use by the
* WRITE_READ_PARSE_PLAN_TREES test code, which doesn't want to cause
* any change in the node contents.
*
*-------------------------------------------------------------------------
*/
@@ -51,7 +55,7 @@
/* And a few guys need only the pg_strtok support fields */
#define READ_TEMP_LOCALS() \
char *token; \
const char *token; \
int length
/* ... but most need both */
@@ -120,12 +124,19 @@
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = nullable_string(token, length)
/* Read a parse location field (and throw away the value, per notes above) */
/* Read a parse location field (and possibly throw away the value) */
#ifdef WRITE_READ_PARSE_PLAN_TREES
#define READ_LOCATION_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = restore_location_fields ? atoi(token) : -1
#else
#define READ_LOCATION_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
(void) token; /* in case not used elsewhere */ \
local_node->fldname = -1 /* set field to "unknown" */
#endif
/* Read a Node field */
#define READ_NODE_FIELD(fldname) \
@@ -2804,7 +2815,7 @@ readDatum(bool typbyval)
Size length,
i;
int tokenLength;
char *token;
const char *token;
Datum res;
char *s;
@@ -2817,7 +2828,7 @@ readDatum(bool typbyval)
token = pg_strtok(&tokenLength); /* read the '[' */
if (token == NULL || token[0] != '[')
elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu",
token ? (const char *) token : "[NULL]", length);
token ? token : "[NULL]", length);
if (typbyval)
{
@@ -2847,7 +2858,7 @@ readDatum(bool typbyval)
token = pg_strtok(&tokenLength); /* read the ']' */
if (token == NULL || token[0] != ']')
elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu",
token ? (const char *) token : "[NULL]", length);
token ? token : "[NULL]", length);
return res;
}
@@ -2860,7 +2871,7 @@ readAttrNumberCols(int numCols)
{
int tokenLength,
i;
char *token;
const char *token;
AttrNumber *attr_vals;
if (numCols <= 0)
@@ -2884,7 +2895,7 @@ readOidCols(int numCols)
{
int tokenLength,
i;
char *token;
const char *token;
Oid *oid_vals;
if (numCols <= 0)
@@ -2908,7 +2919,7 @@ readIntCols(int numCols)
{
int tokenLength,
i;
char *token;
const char *token;
int *int_vals;
if (numCols <= 0)
@@ -2932,7 +2943,7 @@ readBoolCols(int numCols)
{
int tokenLength,
i;
char *token;
const char *token;
bool *bool_vals;
if (numCols <= 0)