/* * stringToNode - * returns a Node with a given legal ASCII representation */ void * stringToNode(char *str) { char *save_strtok; char *save_begin = pg_strtok_begin; void *retval; /* * 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. */ save_strtok = pg_strtok_ptr; pg_strtok_ptr = str; /* point pg_strtok at the string to read */ pg_strtok_begin = str; /* CDB: save starting position for debug */ retval = nodeRead(NULL, 0); /* do the reading */ pg_strtok_ptr = save_strtok; pg_strtok_begin = save_begin; return retval; }
/* * stringToNode - * 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. */ static void * stringToNodeInternal(const char *str, bool restore_loc_fields) { 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 * 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. */ save_strtok = pg_strtok_ptr; 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; }
/* * nodeRead - * Slightly higher-level reader. * * This routine applies some semantic knowledge on top of the purely * lexical tokenizer pg_strtok(). It can read * * Value token nodes (integers, floats, or strings); * * General nodes (via parseNodeString() from readfuncs.c); * * Lists of the above; * * Lists of integers or OIDs. * The return value is declared void *, not Node *, to avoid having to * cast it explicitly in callers that assign to fields of different types. * * External callers should always pass NULL/0 for the arguments. Internally * a non-NULL token may be passed when the upper recursion level has already * scanned the first token of a node's representation. * * We assume pg_strtok is already initialized with a string to read (hence * this should only be invoked from within a stringToNode operation). */ void * nodeRead(char *token, int tok_len) { Node *result; NodeTag type; if (token == NULL) /* need to read a token? */ { token = pg_strtok(&tok_len); if (token == NULL) /* end of input */ return NULL; } type = nodeTokenType(token, tok_len); switch (type) { case LEFT_BRACE: result = parseNodeString(); token = pg_strtok(&tok_len); if (token == NULL || token[0] != '}') elog(ERROR, "did not find '}' at end of input node"); break; case LEFT_PAREN: { List *l = NIL; /*---------- * Could be an integer list: (i int int ...) * or an OID list: (o int int ...) * or a list of nodes/values: (node node ...) *---------- */ token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (tok_len == 1 && token[0] == 'i') { /* List of integers */ for (;;) { int val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (int) strtol(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized integer: \"%.*s\"", tok_len, token); l = lappend_int(l, val); } } else if (tok_len == 1 && token[0] == 'o') { /* List of OIDs */ for (;;) { Oid val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (Oid) strtoul(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized OID: \"%.*s\"", tok_len, token); l = lappend_oid(l, val); } } else { /* List of other node types */ for (;;) { /* We have already scanned next token... */ if (token[0] == ')') break; l = lappend(l, nodeRead(token, tok_len)); token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); } } result = (Node *) l; break; } case RIGHT_PAREN: elog(ERROR, "unexpected right parenthesis"); result = NULL; /* keep compiler happy */ break; case OTHER_TOKEN: if (tok_len == 0) { /* must be "<>" --- represents a null pointer */ result = NULL; } else { elog(ERROR, "unrecognized token: \"%.*s\"", tok_len, token); result = NULL; /* keep compiler happy */ } break; case T_Integer: /* * we know that the token terminates on a char atol will stop at */ result = (Node *) makeInteger(atol(token)); break; case T_Float: { char *fval = (char *) palloc(tok_len + 1); memcpy(fval, token, tok_len); fval[tok_len] = '\0'; result = (Node *) makeFloat(fval); } break; case T_String: /* need to remove leading and trailing quotes, and backslashes */ result = (Node *) makeString(debackslash(token + 1, tok_len - 2)); break; case T_BitString: { char *val = palloc(tok_len); /* skip leading 'b' */ strncpy(val, token + 1, tok_len - 1); val[tok_len - 1] = '\0'; result = (Node *) makeBitString(val); break; } default: elog(ERROR, "unrecognized node type: %d", (int) type); result = NULL; /* keep compiler happy */ break; } return (void *) result; }
/* * nodeRead - * Slightly higher-level reader. * * This routine applies some semantic knowledge on top of the purely * lexical tokenizer pg_strtok(). It can read * * Value token nodes (integers, floats, or strings); * * General nodes (via parseNodeString() from readfuncs.c); * * Lists of the above; * * Lists of integers or OIDs. * The return value is declared void *, not Node *, to avoid having to * cast it explicitly in callers that assign to fields of different types. * * External callers should always pass NULL/0 for the arguments. Internally * a non-NULL token may be passed when the upper recursion level has already * scanned the first token of a node's representation. * * We assume pg_strtok is already initialized with a string to read (hence * this should only be invoked from within a stringToNode operation). */ void * nodeRead(char *token, int tok_len) { Node *result; NodeTag type; if (token == NULL) /* need to read a token? */ { token = pg_strtok(&tok_len); if (token == NULL) /* end of input */ return NULL; } type = nodeTokenType(token, tok_len); switch ((int)type) { case LEFT_BRACE: result = parseNodeString(); token = pg_strtok(&tok_len); /* * CDB: Check for extra fields left over following the ones that * were consumed by the node reader function. If this tree was * read from the catalog, it might have been stored by a future * release which may have added fields that we don't know about. */ while (token && token[0] == ':') { /* * Check for special :prereq tag that a future release may have * inserted to tell us that the node's semantics are not * downward compatible. The node reader function should have * consumed any such tags for features that it supports. If a * :prereq is left to be seen here, that means its feature isn't * implemented in this release and we must reject the statement. * The tag should be followed by a concise feature name or * release id that can be shown to the user in an error message. */ if (tok_len == 7 && memcmp(token, ":prereq", 7) == 0) { token = pg_strtok(&tok_len); token = debackslash(token, tok_len); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("This operation requires a feature " "called \"%.*s\" which is not " "supported in this version of %s.", tok_len, token, PACKAGE_NAME) )); } /* * Other extra fields can safely be ignored. They are assumed * downward compatible unless a :prereq tag tells us otherwise. */ nodeReadSkip(); ereport(DEBUG2, (errmsg("nodeRead: unknown option '%.*s' ignored", tok_len, token), errdetail("Skipped '%.*s' at offset %d in %s", (int)(pg_strtok_ptr - token), token, (int)(token - pg_strtok_begin), pg_strtok_begin) )); token = pg_strtok(&tok_len); } if (token == NULL ) elog(ERROR, "did not find '}' at end of input node"); if (token[0] != '}') elog(ERROR, "did not find '}' at end of input node, instead found %s",token); break; case LEFT_PAREN: { List *l = NIL; /*---------- * Could be an integer list: (i int int ...) * or an OID list: (o int int ...) * or a list of nodes/values: (node node ...) *---------- */ token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (tok_len == 1 && token[0] == 'i') { /* List of integers */ for (;;) { int val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (int) strtol(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized integer: \"%.*s\"", tok_len, token); l = lappend_int(l, val); } } else if (tok_len == 1 && token[0] == 'o') { /* List of OIDs */ for (;;) { Oid val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (Oid) strtoul(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized OID: \"%.*s\"", tok_len, token); l = lappend_oid(l, val); } } else { /* List of other node types */ for (;;) { /* We have already scanned next token... */ if (token[0] == ')') break; l = lappend(l, nodeRead(token, tok_len)); token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); } } result = (Node *) l; break; } case RIGHT_PAREN: elog(ERROR, "unexpected right parenthesis"); result = NULL; /* keep compiler happy */ break; case OTHER_TOKEN: if (tok_len == 0) { /* must be "<>" --- represents a null pointer */ result = NULL; } else { elog(ERROR, "unrecognized token: \"%.*s\"", tok_len, token); result = NULL; /* keep compiler happy */ } break; case T_Integer: /* * we know that the token terminates on a char atol will stop at */ result = (Node *) makeInteger(atol(token)); break; case T_Float: { char *fval = (char *) palloc(tok_len + 1); memcpy(fval, token, tok_len); fval[tok_len] = '\0'; result = (Node *) makeFloat(fval); } break; case T_String: /* need to remove leading and trailing quotes, and backslashes */ result = (Node *) makeString(debackslash(token + 1, tok_len - 2)); break; case T_BitString: { char *val = palloc(tok_len); /* skip leading 'b' */ memcpy(val, token + 1, tok_len - 1); val[tok_len - 1] = '\0'; result = (Node *) makeBitString(val); break; } default: elog(ERROR, "unrecognized node type: %d", (int) type); result = NULL; /* keep compiler happy */ break; } return (void *) result; }