mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +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:
@ -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)
|
||||
|
Reference in New Issue
Block a user