diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/nodes/read.c | 68 | ||||
-rw-r--r-- | src/backend/nodes/readfuncs.c | 31 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 1 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 74 | ||||
-rw-r--r-- | src/include/nodes/nodes.h | 5 | ||||
-rw-r--r-- | src/include/nodes/readfuncs.h | 13 | ||||
-rw-r--r-- | src/include/pg_config_manual.h | 7 |
7 files changed, 170 insertions, 29 deletions
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c index a775f9120ee..99ed2f248a2 100644 --- a/src/backend/nodes/read.c +++ b/src/backend/nodes/read.c @@ -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; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 81f568b3ee1..519deab63ab 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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) diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index bf5df26009a..60b8de0f95d 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1335,7 +1335,6 @@ addRangeTableEntryForSubquery(ParseState *pstate, Assert(pstate != NULL); rte->rtekind = RTE_SUBQUERY; - rte->relid = InvalidOid; rte->subquery = subquery; rte->alias = alias; 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. */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 697d3d7a5fd..cac6ff0eda4 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -610,7 +610,10 @@ extern char *bmsToString(const struct Bitmapset *bms); /* * nodes/{readfuncs.c,read.c} */ -extern void *stringToNode(char *str); +extern void *stringToNode(const char *str); +#ifdef WRITE_READ_PARSE_PLAN_TREES +extern void *stringToNodeWithLocations(const char *str); +#endif extern struct Bitmapset *readBitmapset(void); extern uintptr_t readDatum(bool typbyval); extern bool *readBoolCols(int numCols); diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h index 491e61c459f..4f0d3c2192d 100644 --- a/src/include/nodes/readfuncs.h +++ b/src/include/nodes/readfuncs.h @@ -17,11 +17,18 @@ #include "nodes/nodes.h" /* + * variable in read.c that needs to be accessible to readfuncs.c + */ +#ifdef WRITE_READ_PARSE_PLAN_TREES +extern bool restore_location_fields; +#endif + +/* * prototypes for functions in read.c (the lisp token parser) */ -extern char *pg_strtok(int *length); -extern char *debackslash(char *token, int length); -extern void *nodeRead(char *token, int tok_len); +extern const char *pg_strtok(int *length); +extern char *debackslash(const char *token, int length); +extern void *nodeRead(const char *token, int tok_len); /* * prototypes for functions in readfuncs.c diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index b309395f11c..b0365254f65 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -287,6 +287,13 @@ /* #define COPY_PARSE_PLAN_TREES */ /* + * Define this to force all parse and plan trees to be passed through + * outfuncs.c/readfuncs.c, to facilitate catching errors and omissions in + * those modules. + */ +/* #define WRITE_READ_PARSE_PLAN_TREES */ + +/* * Define this to force all raw parse trees for DML statements to be scanned * by raw_expression_tree_walker(), to facilitate catching errors and * omissions in that function. |