From 2e8e0658aa1cbe4ab7f0a79757806da262bfe133 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Fri, 14 Aug 2026 16:01:54 +0200 Subject: [PATCH v1] Make stringToNode infrastructure thread-safe Whilst stringToNode was _technically_ reentrant-safe, this was only for single-threaded workloads; the string we're decoding was still stored in a global variable. This patch moves the global variables into a struct that's passed around on the stack, making this part of the code completely thread-safe. --- src/backend/nodes/gen_node_support.pl | 8 +- src/backend/nodes/read.c | 65 ++++-------- src/backend/nodes/readfuncs.c | 144 +++++++++++++------------- src/include/nodes/nodes.h | 15 +-- src/include/nodes/readfuncs.h | 17 +-- 5 files changed, 116 insertions(+), 133 deletions(-) diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index f4b1317e99f..0b766272018 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -909,7 +909,7 @@ foreach my $n (@node_types) print $rfs "\tif (MATCH(\"$N\", " . length($N) . "))\n" - . "\t\treturn (Node *) _read${n}();\n" + . "\t\treturn (Node *) _read${n}(ctx);\n" unless $no_read; next if elem $n, @custom_read_write; @@ -930,7 +930,7 @@ _out${n}(StringInfo str, const $n *node) : 'READ_LOCALS_NO_FIELDS'; print $rff " static $n * -_read${n}(void) +_read${n}(ReadNodeContext *ctx) { \t$macro($n); @@ -1192,8 +1192,8 @@ _read${n}(void) /* Lookup CustomScanMethods by CustomName */ char *custom_name; const CustomScanMethods *methods; - token = pg_strtok(&length); /* skip methods: */ - token = pg_strtok(&length); /* CustomName */ + token = pg_strtok(ctx, &length); /* skip methods: */ + token = pg_strtok(ctx, &length); /* CustomName */ custom_name = nullable_string(token, length); methods = GetCustomScanMethods(custom_name, false); local_node->methods = methods; diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c index f85cf65ea48..bacfc9b4640 100644 --- a/src/backend/nodes/read.c +++ b/src/backend/nodes/read.c @@ -28,15 +28,6 @@ #include "nodes/value.h" -/* Static state for pg_strtok */ -static const char *pg_strtok_ptr = NULL; - -/* State flag that determines how readfuncs.c should treat location fields */ -#ifdef DEBUG_NODE_TESTS_ENABLED -bool restore_location_fields = false; -#endif - - /* * stringToNode - * builds a Node tree from its string representation (assumed valid) @@ -49,36 +40,22 @@ static void * stringToNodeInternal(const char *str, bool restore_loc_fields) { void *retval; - const char *save_strtok; -#ifdef DEBUG_NODE_TESTS_ENABLED - bool save_restore_location_fields; -#endif + ReadNodeContext ctx; /* - * We save and restore the pre-existing state of pg_strtok. This makes the - * world safe for re-entrant invocation of stringToNode, without incurring - * a lot of notational overhead by having to pass the next-character - * pointer around through all the readfuncs.c code. + * We don't have a separate type for const StringInfos, unconstify + + * careful coding will have to suffice. */ - save_strtok = pg_strtok_ptr; - - pg_strtok_ptr = str; /* point pg_strtok at the string to read */ + ctx.str = str; /* * If enabled, likewise save/restore the location field handling flag. */ #ifdef DEBUG_NODE_TESTS_ENABLED - save_restore_location_fields = restore_location_fields; - restore_location_fields = restore_loc_fields; + ctx.restore_location_fields = restore_loc_fields; #endif - retval = nodeRead(NULL, 0); /* do the reading */ - - pg_strtok_ptr = save_strtok; - -#ifdef DEBUG_NODE_TESTS_ENABLED - restore_location_fields = save_restore_location_fields; -#endif + retval = nodeRead(&ctx, NULL, 0); /* do the reading */ return retval; } @@ -150,12 +127,12 @@ stringToNodeWithLocations(const char *str) * as a single token. */ const char * -pg_strtok(int *length) +pg_strtok(ReadNodeContext *ctx, int *length) { const char *local_str; /* working pointer to string */ const char *ret_str; /* start of token to return */ - local_str = pg_strtok_ptr; + local_str = ctx->str; while (*local_str == ' ' || *local_str == '\n' || *local_str == '\t') local_str++; @@ -163,7 +140,7 @@ pg_strtok(int *length) if (*local_str == '\0') { *length = 0; - pg_strtok_ptr = local_str; + ctx->str = local_str; return NULL; /* no more tokens */ } @@ -200,7 +177,7 @@ pg_strtok(int *length) if (*length == 2 && ret_str[0] == '<' && ret_str[1] == '>') *length = 0; - pg_strtok_ptr = local_str; + ctx->str = local_str; return ret_str; } @@ -317,14 +294,14 @@ nodeTokenType(const char *token, int length) * this should only be invoked from within a stringToNode operation). */ void * -nodeRead(const char *token, int tok_len) +nodeRead(ReadNodeContext *ctx, const char *token, int tok_len) { Node *result; NodeTag type; if (token == NULL) /* need to read a token? */ { - token = pg_strtok(&tok_len); + token = pg_strtok(ctx, &tok_len); if (token == NULL) /* end of input */ return NULL; @@ -335,8 +312,8 @@ nodeRead(const char *token, int tok_len) switch ((int) type) { case LEFT_BRACE: - result = parseNodeString(); - token = pg_strtok(&tok_len); + result = parseNodeString(ctx); + token = pg_strtok(ctx, &tok_len); if (token == NULL || token[0] != '}') elog(ERROR, "did not find '}' at end of input node"); break; @@ -352,7 +329,7 @@ nodeRead(const char *token, int tok_len) * or a list of nodes/values: (node node ...) *---------- */ - token = pg_strtok(&tok_len); + token = pg_strtok(ctx, &tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (tok_len == 1 && token[0] == 'i') @@ -363,7 +340,7 @@ nodeRead(const char *token, int tok_len) int val; char *endptr; - token = pg_strtok(&tok_len); + token = pg_strtok(ctx, &tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') @@ -384,7 +361,7 @@ nodeRead(const char *token, int tok_len) Oid val; char *endptr; - token = pg_strtok(&tok_len); + token = pg_strtok(ctx, &tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') @@ -405,7 +382,7 @@ nodeRead(const char *token, int tok_len) TransactionId val; char *endptr; - token = pg_strtok(&tok_len); + token = pg_strtok(ctx, &tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') @@ -428,7 +405,7 @@ nodeRead(const char *token, int tok_len) int val; char *endptr; - token = pg_strtok(&tok_len); + token = pg_strtok(ctx, &tok_len); if (token == NULL) elog(ERROR, "unterminated Bitmapset structure"); if (tok_len == 1 && token[0] == ')') @@ -449,8 +426,8 @@ nodeRead(const char *token, int tok_len) /* We have already scanned next token... */ if (token[0] == ')') break; - l = lappend(l, nodeRead(token, tok_len)); - token = pg_strtok(&tok_len); + l = lappend(l, nodeRead(ctx, token, tok_len)); + token = pg_strtok(ctx, &tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b6b2ce6c792..6679e99fce0 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -56,77 +56,77 @@ /* Read an integer field (anything written as ":fldname %d") */ #define READ_INT_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = atoi(token) /* Read an unsigned integer field (anything written as ":fldname %u") */ #define READ_UINT_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = atoui(token) /* Read a signed integer field (anything written using INT64_FORMAT) */ #define READ_INT64_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = strtoi64(token, NULL, 10) /* Read an unsigned integer field (anything written using UINT64_FORMAT) */ #define READ_UINT64_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = strtou64(token, NULL, 10) /* Read a long integer field (anything written as ":fldname %ld") */ #define READ_LONG_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = atol(token) /* Read an OID field (don't hard-wire assumption that OID is same as uint) */ #define READ_OID_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = atooid(token) /* Read a char field (ie, one ascii character) */ #define READ_CHAR_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ /* avoid overhead of calling debackslash() for one char */ \ local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0]) /* Read an enumerated-type field that was written as an integer code */ #define READ_ENUM_FIELD(fldname, enumtype) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = (enumtype) atoi(token) /* Read a float field */ #define READ_FLOAT_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = atof(token) /* Read a boolean field */ #define READ_BOOL_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = strtobool(token) /* Read a character-string field */ #define READ_STRING_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ - token = pg_strtok(&length); /* get field value */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ local_node->fldname = nullable_string(token, length) /* Read a parse location field (and possibly throw away the value) */ #ifdef DEBUG_NODE_TESTS_ENABLED #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 + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* get field value */ \ + local_node->fldname = ctx->restore_location_fields ? atoi(token) : -1 #else #define READ_LOCATION_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ @@ -137,35 +137,35 @@ /* Read a Node field */ #define READ_NODE_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ (void) token; /* in case not used elsewhere */ \ - local_node->fldname = nodeRead(NULL, 0) + local_node->fldname = nodeRead(ctx, NULL, 0) /* Read a bitmapset field */ #define READ_BITMAPSET_FIELD(fldname) \ - token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(ctx, &length); /* skip :fldname */ \ (void) token; /* in case not used elsewhere */ \ - local_node->fldname = _readBitmapset() + local_node->fldname = _readBitmapset(ctx) /* Read an attribute number array */ #define READ_ATTRNUMBER_ARRAY(fldname, len) \ - token = pg_strtok(&length); /* skip :fldname */ \ - local_node->fldname = readAttrNumberCols(len) + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + local_node->fldname = readAttrNumberCols(ctx, len) /* Read an oid array */ #define READ_OID_ARRAY(fldname, len) \ - token = pg_strtok(&length); /* skip :fldname */ \ - local_node->fldname = readOidCols(len) + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + local_node->fldname = readOidCols(ctx, len) /* Read an int array */ #define READ_INT_ARRAY(fldname, len) \ - token = pg_strtok(&length); /* skip :fldname */ \ - local_node->fldname = readIntCols(len) + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + local_node->fldname = readIntCols(ctx, len) /* Read a bool array */ #define READ_BOOL_ARRAY(fldname, len) \ - token = pg_strtok(&length); /* skip :fldname */ \ - local_node->fldname = readBoolCols(len) + token = pg_strtok(ctx, &length); /* skip :fldname */ \ + local_node->fldname = readBoolCols(ctx, len) /* Routine exit */ #define READ_DONE() \ @@ -204,19 +204,19 @@ nullable_string(const char *token, int length) * Bitmapset when we come across one in other contexts. */ static Bitmapset * -_readBitmapset(void) +_readBitmapset(ReadNodeContext *ctx) { Bitmapset *result = NULL; READ_TEMP_LOCALS(); - token = pg_strtok(&length); + token = pg_strtok(ctx, &length); if (token == NULL) elog(ERROR, "incomplete Bitmapset structure"); if (length != 1 || token[0] != '(') elog(ERROR, "unrecognized token: \"%.*s\"", length, token); - token = pg_strtok(&length); + token = pg_strtok(ctx, &length); if (token == NULL) elog(ERROR, "incomplete Bitmapset structure"); if (length != 1 || token[0] != 'b') @@ -227,7 +227,7 @@ _readBitmapset(void) int val; char *endptr; - token = pg_strtok(&length); + token = pg_strtok(ctx, &length); if (token == NULL) elog(ERROR, "unterminated Bitmapset structure"); if (length == 1 && token[0] == ')') @@ -246,9 +246,9 @@ _readBitmapset(void) * That's somewhat historical, though, because calling nodeRead() will work. */ Bitmapset * -readBitmapset(void) +readBitmapset(ReadNodeContext *ctx) { - return _readBitmapset(); + return _readBitmapset(ctx); } #include "readfuncs.funcs.c" @@ -260,7 +260,7 @@ readBitmapset(void) */ static Const * -_readConst(void) +_readConst(ReadNodeContext *ctx) { READ_LOCALS(Const); @@ -272,23 +272,23 @@ _readConst(void) READ_BOOL_FIELD(constisnull); READ_LOCATION_FIELD(location); - token = pg_strtok(&length); /* skip :constvalue */ + token = pg_strtok(ctx, &length); /* skip :constvalue */ if (local_node->constisnull) - token = pg_strtok(&length); /* skip "<>" */ + token = pg_strtok(ctx, &length); /* skip "<>" */ else - local_node->constvalue = readDatum(local_node->constbyval); + local_node->constvalue = readDatum(ctx, local_node->constbyval); READ_DONE(); } static BoolExpr * -_readBoolExpr(void) +_readBoolExpr(ReadNodeContext *ctx) { READ_LOCALS(BoolExpr); /* do-it-yourself enum representation */ - token = pg_strtok(&length); /* skip :boolop */ - token = pg_strtok(&length); /* get field value */ + token = pg_strtok(ctx, &length); /* skip :boolop */ + token = pg_strtok(ctx, &length); /* get field value */ if (length == 3 && strncmp(token, "and", 3) == 0) local_node->boolop = AND_EXPR; else if (length == 2 && strncmp(token, "or", 2) == 0) @@ -305,17 +305,17 @@ _readBoolExpr(void) } static A_Const * -_readA_Const(void) +_readA_Const(ReadNodeContext *ctx) { READ_LOCALS(A_Const); /* We expect either NULL or :val here */ - token = pg_strtok(&length); + token = pg_strtok(ctx, &length); if (length == 4 && strncmp(token, "NULL", 4) == 0) local_node->isnull = true; else { - union ValUnion *tmp = nodeRead(NULL, 0); + union ValUnion *tmp = nodeRead(ctx, NULL, 0); /* To forestall valgrind complaints, copy only the valid data */ switch (nodeTag(tmp)) @@ -348,7 +348,7 @@ _readA_Const(void) } static RangeTblEntry * -_readRangeTblEntry(void) +_readRangeTblEntry(ReadNodeContext *ctx) { READ_LOCALS(RangeTblEntry); @@ -452,11 +452,11 @@ _readRangeTblEntry(void) } static A_Expr * -_readA_Expr(void) +_readA_Expr(ReadNodeContext *ctx) { READ_LOCALS(A_Expr); - token = pg_strtok(&length); + token = pg_strtok(ctx, &length); if (length == 3 && strncmp(token, "ANY", 3) == 0) { @@ -526,7 +526,7 @@ _readA_Expr(void) else if (length == 5 && strncmp(token, ":name", 5) == 0) { local_node->kind = AEXPR_OP; - local_node->name = nodeRead(NULL, 0); + local_node->name = nodeRead(ctx, NULL, 0); } else elog(ERROR, "unrecognized A_Expr kind: \"%.*s\"", length, token); @@ -541,7 +541,7 @@ _readA_Expr(void) } static ExtensibleNode * -_readExtensibleNode(void) +_readExtensibleNode(ReadNodeContext *ctx) { const ExtensibleNodeMethods *methods; ExtensibleNode *local_node; @@ -549,8 +549,8 @@ _readExtensibleNode(void) READ_TEMP_LOCALS(); - token = pg_strtok(&length); /* skip :extnodename */ - token = pg_strtok(&length); /* get extnodename */ + token = pg_strtok(ctx, &length); /* skip :extnodename */ + token = pg_strtok(ctx, &length); /* get extnodename */ extnodename = nullable_string(token, length); if (!extnodename) @@ -577,14 +577,14 @@ _readExtensibleNode(void) * The string to be read must already have been loaded into pg_strtok(). */ Node * -parseNodeString(void) +parseNodeString(ReadNodeContext *ctx) { READ_TEMP_LOCALS(); /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); - token = pg_strtok(&length); + token = pg_strtok(ctx, &length); #define MATCH(tokname, namelen) \ (length == namelen && memcmp(token, tokname, namelen) == 0) @@ -604,7 +604,7 @@ parseNodeString(void) * so we must be told that. */ Datum -readDatum(bool typbyval) +readDatum(ReadNodeContext *ctx, bool typbyval) { Size length; int tokenLength; @@ -615,10 +615,10 @@ readDatum(bool typbyval) /* * read the actual length of the value */ - token = pg_strtok(&tokenLength); + token = pg_strtok(ctx, &tokenLength); length = atoui(token); - token = pg_strtok(&tokenLength); /* read the '[' */ + token = pg_strtok(ctx, &tokenLength); /* read the '[' */ if (token == NULL || token[0] != '[') elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu", token ? token : "[NULL]", length); @@ -631,7 +631,7 @@ readDatum(bool typbyval) s = (char *) (&res); for (Size i = 0; i < (Size) sizeof(Datum); i++) { - token = pg_strtok(&tokenLength); + token = pg_strtok(ctx, &tokenLength); s[i] = (char) atoi(token); } } @@ -642,13 +642,13 @@ readDatum(bool typbyval) s = (char *) palloc(length); for (Size i = 0; i < length; i++) { - token = pg_strtok(&tokenLength); + token = pg_strtok(ctx, &tokenLength); s[i] = (char) atoi(token); } res = PointerGetDatum(s); } - token = pg_strtok(&tokenLength); /* read the ']' */ + token = pg_strtok(ctx, &tokenLength); /* read the ']' */ if (token == NULL || token[0] != ']') elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu", token ? token : "[NULL]", length); @@ -666,11 +666,11 @@ readDatum(bool typbyval) */ #define READ_SCALAR_ARRAY(fnname, datatype, convfunc) \ datatype * \ -fnname(int numCols) \ +fnname(ReadNodeContext *ctx, int numCols) \ { \ datatype *vals; \ READ_TEMP_LOCALS(); \ - token = pg_strtok(&length); \ + token = pg_strtok(ctx, &length); \ if (token == NULL) \ elog(ERROR, "incomplete scalar array"); \ if (length == 0) \ @@ -680,12 +680,12 @@ fnname(int numCols) \ vals = (datatype *) palloc(numCols * sizeof(datatype)); \ for (int i = 0; i < numCols; i++) \ { \ - token = pg_strtok(&length); \ + token = pg_strtok(ctx, &length); \ if (token == NULL || token[0] == ')') \ elog(ERROR, "incomplete scalar array"); \ vals[i] = convfunc(token); \ } \ - token = pg_strtok(&length); \ + token = pg_strtok(ctx, &length); \ if (token == NULL || length != 1 || token[0] != ')') \ elog(ERROR, "incomplete scalar array"); \ return vals; \ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 372eee20680..be29644b431 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -207,16 +207,19 @@ extern char *bmsToString(const struct Bitmapset *bms); /* * nodes/{readfuncs.c,read.c} */ + +typedef struct ReadNodeContext ReadNodeContext; + extern void *stringToNode(const char *str); #ifdef DEBUG_NODE_TESTS_ENABLED extern void *stringToNodeWithLocations(const char *str); #endif -extern struct Bitmapset *readBitmapset(void); -extern Datum readDatum(bool typbyval); -extern bool *readBoolCols(int numCols); -extern int *readIntCols(int numCols); -extern Oid *readOidCols(int numCols); -extern int16 *readAttrNumberCols(int numCols); +extern struct Bitmapset *readBitmapset(ReadNodeContext *ctx); +extern Datum readDatum(ReadNodeContext *ctx, bool typbyval); +extern bool *readBoolCols(ReadNodeContext *ctx, int numCols); +extern int *readIntCols(ReadNodeContext *ctx, int numCols); +extern Oid *readOidCols(ReadNodeContext *ctx, int numCols); +extern int16 *readAttrNumberCols(ReadNodeContext *ctx, int numCols); /* * nodes/copyfuncs.c diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h index 0a5abdd9691..92ca3a800e6 100644 --- a/src/include/nodes/readfuncs.h +++ b/src/include/nodes/readfuncs.h @@ -16,23 +16,26 @@ #include "nodes/nodes.h" -/* - * variable in read.c that needs to be accessible to readfuncs.c - */ +typedef struct ReadNodeContext +{ + /* the string that's being parsed */ + const char *str; #ifdef DEBUG_NODE_TESTS_ENABLED -extern PGDLLIMPORT bool restore_location_fields; + /* state flag determining how readfuncs.c should treat location fields */ + bool restore_location_fields; #endif +} ReadNodeContext; /* * prototypes for functions in read.c (the lisp token parser) */ -extern const char *pg_strtok(int *length); +extern const char *pg_strtok(ReadNodeContext *ctx, int *length); extern char *debackslash(const char *token, int length); -extern void *nodeRead(const char *token, int tok_len); +extern void *nodeRead(ReadNodeContext *ctx, const char *token, int tok_len); /* * prototypes for functions in readfuncs.c */ -extern Node *parseNodeString(void); +extern Node *parseNodeString(ReadNodeContext *ctx); #endif /* READFUNCS_H */ -- 2.50.1 (Apple Git-155)