* routines to convert a string (legal ascii representation of node) back
* to nodes
*
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.29 2001/03/22 03:59:32 momjian Exp $
+ * src/backend/nodes/read.c
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
#include "postgres.h"
#include <ctype.h>
-#include <errno.h>
#include "nodes/pg_list.h"
#include "nodes/readfuncs.h"
+#include "nodes/value.h"
/* Static state for pg_strtok */
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.
+ * 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 */
- retval = nodeRead(true); /* do the reading */
+ retval = nodeRead(NULL, 0); /* do the reading */
pg_strtok_ptr = save_strtok;
* Backslashes themselves must also be backslashed for consistency.
* Any other character can be, but need not be, backslashed as well.
* * If the resulting token is '<>' (with no backslash), it is returned
- * as a non-NULL pointer to the token but with length == 0. Note that
+ * as a non-NULL pointer to the token but with length == 0. Note that
* there is no other way to get a zero-length token.
*
* Returns a pointer to the start of the next token, and the length of the
- * token (including any embedded backslashes!) in *length. If there are
+ * token (including any embedded backslashes!) in *length. If there are
* no more tokens, NULL and 0 are returned.
*
* NOTE: this routine doesn't remove backslashes; the caller must do so
* if necessary (see "debackslash").
*
* NOTE: prior to release 7.0, this routine also had a special case to treat
- * a token starting with '"' as extending to the next '"'. This code was
+ * a token starting with '"' as extending to the next '"'. This code was
* broken, however, since it would fail to cope with a string containing an
* embedded '"'. I have therefore removed this special case, and instead
- * introduced rules for using backslashes to quote characters. Higher-level
+ * introduced rules for using backslashes to quote characters. Higher-level
* code should add backslashes to a string constant to ensure it is treated
* as a single token.
*/
#define RIGHT_PAREN (1000000 + 1)
#define LEFT_PAREN (1000000 + 2)
-#define PLAN_SYM (1000000 + 3)
-#define AT_SYMBOL (1000000 + 4)
-#define ATOM_TOKEN (1000000 + 5)
+#define LEFT_BRACE (1000000 + 3)
+#define OTHER_TOKEN (1000000 + 4)
/*
* nodeTokenType -
* It returns one of the following valid NodeTags:
* T_Integer, T_Float, T_String, T_BitString
* and some of its own:
- * RIGHT_PAREN, LEFT_PAREN, PLAN_SYM, AT_SYMBOL, ATOM_TOKEN
+ * RIGHT_PAREN, LEFT_PAREN, LEFT_BRACE, OTHER_TOKEN
*
* Assumption: the ascii representation is legal
*/
NodeTag retval;
char *numptr;
int numlen;
- char *endptr;
/*
* Check if the token is a number
if (*numptr == '+' || *numptr == '-')
numptr++, numlen--;
if ((numlen > 0 && isdigit((unsigned char) *numptr)) ||
- (numlen > 1 && *numptr == '.' && isdigit((unsigned char) numptr[1])))
+ (numlen > 1 && *numptr == '.' && isdigit((unsigned char) numptr[1])))
{
-
/*
* Yes. Figure out whether it is integral or float; this requires
- * both a syntax check and a range check. strtol() can do both for
- * us. We know the token will end at a character that strtol will
- * stop at, so we do not need to modify the string.
+ * both a syntax check and a range check. strtol() can do both for us.
+ * We know the token will end at a character that strtol will stop at,
+ * so we do not need to modify the string.
*/
+ long val;
+ char *endptr;
+
errno = 0;
- (void) strtol(token, &endptr, 10);
- if (endptr != token + length || errno == ERANGE)
+ val = strtol(token, &endptr, 10);
+ if (endptr != token + length || errno == ERANGE ||
+ /* check for overflow of int */
+ val != (int) val)
return T_Float;
return T_Integer;
}
else if (*token == ')')
retval = RIGHT_PAREN;
else if (*token == '{')
- retval = PLAN_SYM;
- else if (*token == '@' && length == 1)
- retval = AT_SYMBOL;
- else if (*token == '\"' && length > 1 && token[length - 1] == '\"')
+ retval = LEFT_BRACE;
+ else if (*token == '"' && length > 1 && token[length - 1] == '"')
retval = T_String;
else if (*token == 'b')
retval = T_BitString;
else
- retval = ATOM_TOKEN;
+ retval = OTHER_TOKEN;
return retval;
}
* Slightly higher-level reader.
*
* This routine applies some semantic knowledge on top of the purely
- * lexical tokenizer pg_strtok(). It can read
+ * lexical tokenizer pg_strtok(). It can read
* * Value token nodes (integers, floats, or strings);
- * * Plan nodes (via parsePlanString() from readfuncs.c);
- * * Lists of the above.
+ * * 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).
- * Any callers should set read_car_only to true.
*/
void *
-nodeRead(bool read_car_only)
+nodeRead(char *token, int tok_len)
{
- char *token;
- int tok_len;
+ Node *result;
NodeTag type;
- Node *this_value,
- *return_value;
- bool make_dotted_pair_cell = false;
- token = pg_strtok(&tok_len);
+ if (token == NULL) /* need to read a token? */
+ {
+ token = pg_strtok(&tok_len);
- if (token == NULL)
- return NULL;
+ if (token == NULL) /* end of input */
+ return NULL;
+ }
type = nodeTokenType(token, tok_len);
- switch (type)
+ switch ((int) type)
{
- case PLAN_SYM:
- this_value = parsePlanString();
+ case LEFT_BRACE:
+ result = parseNodeString();
token = pg_strtok(&tok_len);
if (token == NULL || token[0] != '}')
- elog(ERROR, "nodeRead: did not find '}' at end of plan node");
- if (!read_car_only)
- make_dotted_pair_cell = true;
- else
- make_dotted_pair_cell = false;
+ elog(ERROR, "did not find '}' at end of input node");
break;
case LEFT_PAREN:
- if (!read_car_only)
{
- List *l = makeNode(List);
+ List *l = NIL;
- lfirst(l) = nodeRead(false);
- lnext(l) = nodeRead(false);
- this_value = (Node *) l;
+ /*----------
+ * 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;
}
- else
- this_value = nodeRead(false);
- break;
case RIGHT_PAREN:
- this_value = NULL;
- break;
- case AT_SYMBOL:
- this_value = NULL;
+ elog(ERROR, "unexpected right parenthesis");
+ result = NULL; /* keep compiler happy */
break;
- case ATOM_TOKEN:
+ case OTHER_TOKEN:
if (tok_len == 0)
{
- /* must be "<>" */
- this_value = NULL;
-
- /*
- * It might be NULL but it is an atom!
- */
- if (read_car_only)
- make_dotted_pair_cell = false;
- else
- make_dotted_pair_cell = true;
+ /* must be "<>" --- represents a null pointer */
+ result = NULL;
}
else
{
- /* !attention! result is not a Node. Use with caution. */
- this_value = (Node *) debackslash(token, tok_len);
- make_dotted_pair_cell = true;
+ 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
+ * we know that the token terminates on a char atoi will stop at
*/
- this_value = (Node *) makeInteger(atol(token));
- make_dotted_pair_cell = true;
+ result = (Node *) makeInteger(atoi(token));
break;
case T_Float:
{
memcpy(fval, token, tok_len);
fval[tok_len] = '\0';
- this_value = (Node *) makeFloat(fval);
- make_dotted_pair_cell = true;
+ result = (Node *) makeFloat(fval);
}
break;
case T_String:
/* need to remove leading and trailing quotes, and backslashes */
- this_value = (Node *) makeString(debackslash(token + 1, tok_len - 2));
- make_dotted_pair_cell = true;
+ 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);
+ memcpy(val, token + 1, tok_len - 1);
val[tok_len - 1] = '\0';
- this_value = (Node *) makeBitString(val);
+ result = (Node *) makeBitString(val);
break;
}
default:
- elog(ERROR, "nodeRead: Bad type %d", type);
- this_value = NULL; /* keep compiler happy */
+ elog(ERROR, "unrecognized node type: %d", (int) type);
+ result = NULL; /* keep compiler happy */
break;
}
- if (make_dotted_pair_cell)
- {
- List *l = makeNode(List);
-
- lfirst(l) = this_value;
- if (!read_car_only)
- lnext(l) = nodeRead(false);
- else
- lnext(l) = NULL;
- return_value = (Node *) l;
- }
- else
- return_value = this_value;
- return return_value;
+ return (void *) result;
}