]> 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 7391942f5c5174ecf895c9fe7acc7e67a0513daf..6e9fa45e37e37ff91c000e48d844d629b2b69f18 100644 (file)
@@ -4,12 +4,12 @@
  *       routines to convert a string (legal ascii representation of node) back
  *       to nodes
  *
- * Portions Copyright (c) 1996-2003, 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.35 2003/08/04 02:39:59 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 */
@@ -41,16 +41,16 @@ stringToNode(char *str)
        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;
 
@@ -85,21 +85,21 @@ 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.
  */
@@ -183,9 +183,8 @@ debackslash(char *token, int length)
 
 #define RIGHT_PAREN (1000000 + 1)
 #define LEFT_PAREN     (1000000 + 2)
-#define NODE_SYM       (1000000 + 3)
-#define AT_SYMBOL      (1000000 + 4)
-#define ATOM_TOKEN     (1000000 + 5)
+#define LEFT_BRACE     (1000000 + 3)
+#define OTHER_TOKEN (1000000 + 4)
 
 /*
  * nodeTokenType -
@@ -193,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, NODE_SYM, AT_SYMBOL, ATOM_TOKEN
+ *             RIGHT_PAREN, LEFT_PAREN, LEFT_BRACE, OTHER_TOKEN
  *
  *       Assumption: the ascii representation is legal
  */
@@ -212,25 +211,22 @@ nodeTokenType(char *token, int length)
        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;
                val = strtol(token, &endptr, 10);
-               if (endptr != token + length || errno == ERANGE
-#ifdef HAVE_LONG_INT_64
-               /* if long > 32 bits, check for overflow of int4 */
-                       || val != (long) ((int32) val)
-#endif
-                       )
+               if (endptr != token + length || errno == ERANGE ||
+                       /* check for overflow of int */
+                       val != (int) val)
                        return T_Float;
                return T_Integer;
        }
@@ -244,15 +240,13 @@ nodeTokenType(char *token, int length)
        else if (*token == ')')
                retval = RIGHT_PAREN;
        else if (*token == '{')
-               retval = NODE_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;
 }
 
@@ -261,91 +255,137 @@ nodeTokenType(char *token, int length)
  *       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);
  *     * General nodes (via parseNodeString() from readfuncs.c);
- *     * Lists of the above.
+ *     * 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 NODE_SYM:
-                       this_value = parseNodeString();
+               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");
-                       if (!read_car_only)
-                               make_dotted_pair_cell = true;
-                       else
-                               make_dotted_pair_cell = false;
                        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;
+                       elog(ERROR, "unexpected right parenthesis");
+                       result = NULL;          /* keep compiler happy */
                        break;
-               case AT_SYMBOL:
-                       this_value = NULL;
-                       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:
                        {
@@ -353,43 +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);
+                               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, "unrecognized node type: %d", (int) type);
-                       this_value = NULL;      /* keep compiler happy */
+                       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;
 }