]> granicus.if.org Git - postgresql/blobdiff - src/backend/nodes/read.c
Change internal integer representation of Value node
[postgresql] / src / backend / nodes / read.c
index df8fcb876d63dc3a3858b9f7f52c34d5108e5216..6e9fa45e37e37ff91c000e48d844d629b2b69f18 100644 (file)
@@ -4,12 +4,12 @@
  *       routines to convert a string (legal ascii representation of node) back
  *       to nodes
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * 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.26 2000/12/03 20:45:33 tgl 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 */
+static char *pg_strtok_ptr = NULL;
+
 
 /*
  * stringToNode -
 void *
 stringToNode(char *str)
 {
+       char       *save_strtok;
        void       *retval;
 
-       lsptok(str, NULL);                      /* set the string used in lsptok */
-       retval = nodeRead(true);        /* start reading */
+       /*
+        * 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(NULL, 0); /* do the reading */
+
+       pg_strtok_ptr = save_strtok;
 
        return retval;
 }
@@ -47,7 +64,7 @@ stringToNode(char *str)
  *****************************************************************************/
 
 /*
- * lsptok --- retrieve next "token" from a string.
+ * pg_strtok --- retrieve next "token" from a string.
  *
  * Works kinda like strtok, except it never modifies the source string.
  * (Instead of storing nulls into the string, the length of the token
@@ -55,9 +72,7 @@ stringToNode(char *str)
  * Also, the rules about what is a token are hard-wired rather than being
  * configured by passing a set of terminating characters.
  *
- * The string is initially set by passing a non-NULL "string" value,
- * and subsequent calls with string==NULL read the previously given value.
- * (Pass length==NULL to set the string without reading its first token.)
+ * The string is assumed to have been initialized already by stringToNode.
  *
  * The rules for tokens are:
  *     * Whitespace (space, tab, newline) always separates tokens.
@@ -70,39 +85,31 @@ stringToNode(char *str)
  *       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.
  */
 char *
-lsptok(char *string, int *length)
+pg_strtok(int *length)
 {
-       static char *saved_str = NULL;
        char       *local_str;          /* working pointer to string */
        char       *ret_str;            /* start of token to return */
 
-       if (string != NULL)
-       {
-               saved_str = string;
-               if (length == NULL)
-                       return NULL;
-       }
-
-       local_str = saved_str;
+       local_str = pg_strtok_ptr;
 
        while (*local_str == ' ' || *local_str == '\n' || *local_str == '\t')
                local_str++;
@@ -110,7 +117,7 @@ lsptok(char *string, int *length)
        if (*local_str == '\0')
        {
                *length = 0;
-               saved_str = local_str;
+               pg_strtok_ptr = local_str;
                return NULL;                    /* no more tokens */
        }
 
@@ -147,7 +154,7 @@ lsptok(char *string, int *length)
        if (*length == 2 && ret_str[0] == '<' && ret_str[1] == '>')
                *length = 0;
 
-       saved_str = local_str;
+       pg_strtok_ptr = local_str;
 
        return ret_str;
 }
@@ -176,9 +183,8 @@ debackslash(char *token, int length)
 
 #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 -
@@ -186,7 +192,7 @@ debackslash(char *token, int length)
  *       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
  */
@@ -196,7 +202,6 @@ nodeTokenType(char *token, int length)
        NodeTag         retval;
        char       *numptr;
        int                     numlen;
-       char       *endptr;
 
        /*
         * Check if the token is a number
@@ -208,22 +213,26 @@ nodeTokenType(char *token, int length)
        if ((numlen > 0 && isdigit((unsigned char) *numptr)) ||
                (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;
        }
 
        /*
-        * these three cases do not need length checks, since lsptok() will
+        * these three cases do not need length checks, since pg_strtok() will
         * always treat them as single-byte tokens
         */
        else if (*token == '(')
@@ -231,15 +240,13 @@ nodeTokenType(char *token, int length)
        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;
 }
 
@@ -248,90 +255,137 @@ nodeTokenType(char *token, int length)
  *       Slightly higher-level reader.
  *
  * This routine applies some semantic knowledge on top of the purely
- * lexical tokenizer lsptok(). 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.
  *
- * Secrets:  He assumes that lsptok already has the string (see above).
- * Any callers should set read_car_only to true.
+ * 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(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 = lsptok(NULL, &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();
-                       token = lsptok(NULL, &tok_len);
-                       if (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;
+               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:
-                       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:
                        {
@@ -339,42 +393,28 @@ nodeRead(bool read_car_only)
 
                                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);
-                       val[tok_len - 1] = '\0';
-                       this_value = (Node *) makeBitString(val);
-                       break;
-               }
+                       {
+                               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, "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;
 }