]> granicus.if.org Git - postgresql/blob - src/backend/nodes/read.c
Remove duplicate include of value.h. I think it got in because Tom and I both added it.
[postgresql] / src / backend / nodes / read.c
1 /*-------------------------------------------------------------------------
2  *
3  * read.c
4  *        routines to convert a string (legal ascii representation of node) back
5  *        to nodes
6  *
7  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/nodes/read.c,v 1.39 2004/01/09 03:07:32 momjian Exp $
13  *
14  * HISTORY
15  *        AUTHOR                        DATE                    MAJOR EVENT
16  *        Andrew Yu                     Nov 2, 1994             file creation
17  *
18  *-------------------------------------------------------------------------
19  */
20 #include "postgres.h"
21
22 #include <ctype.h>
23 #include <errno.h>
24
25 #include "nodes/pg_list.h"
26 #include "nodes/readfuncs.h"
27 #include "nodes/value.h"
28
29
30 /* Static state for pg_strtok */
31 static char *pg_strtok_ptr = NULL;
32
33
34 /*
35  * stringToNode -
36  *        returns a Node with a given legal ASCII representation
37  */
38 void *
39 stringToNode(char *str)
40 {
41         char       *save_strtok;
42         void       *retval;
43
44         /*
45          * We save and restore the pre-existing state of pg_strtok. This makes
46          * the world safe for re-entrant invocation of stringToNode, without
47          * incurring a lot of notational overhead by having to pass the
48          * next-character pointer around through all the readfuncs.c code.
49          */
50         save_strtok = pg_strtok_ptr;
51
52         pg_strtok_ptr = str;            /* point pg_strtok at the string to read */
53
54         retval = nodeRead(true);        /* do the reading */
55
56         pg_strtok_ptr = save_strtok;
57
58         return retval;
59 }
60
61 /*****************************************************************************
62  *
63  * the lisp token parser
64  *
65  *****************************************************************************/
66
67 /*
68  * pg_strtok --- retrieve next "token" from a string.
69  *
70  * Works kinda like strtok, except it never modifies the source string.
71  * (Instead of storing nulls into the string, the length of the token
72  * is returned to the caller.)
73  * Also, the rules about what is a token are hard-wired rather than being
74  * configured by passing a set of terminating characters.
75  *
76  * The string is assumed to have been initialized already by stringToNode.
77  *
78  * The rules for tokens are:
79  *      * Whitespace (space, tab, newline) always separates tokens.
80  *      * The characters '(', ')', '{', '}' form individual tokens even
81  *        without any whitespace around them.
82  *      * Otherwise, a token is all the characters up to the next whitespace
83  *        or occurrence of one of the four special characters.
84  *      * A backslash '\' can be used to quote whitespace or one of the four
85  *        special characters, so that it is treated as a plain token character.
86  *        Backslashes themselves must also be backslashed for consistency.
87  *        Any other character can be, but need not be, backslashed as well.
88  *      * If the resulting token is '<>' (with no backslash), it is returned
89  *        as a non-NULL pointer to the token but with length == 0.      Note that
90  *        there is no other way to get a zero-length token.
91  *
92  * Returns a pointer to the start of the next token, and the length of the
93  * token (including any embedded backslashes!) in *length.      If there are
94  * no more tokens, NULL and 0 are returned.
95  *
96  * NOTE: this routine doesn't remove backslashes; the caller must do so
97  * if necessary (see "debackslash").
98  *
99  * NOTE: prior to release 7.0, this routine also had a special case to treat
100  * a token starting with '"' as extending to the next '"'.      This code was
101  * broken, however, since it would fail to cope with a string containing an
102  * embedded '"'.  I have therefore removed this special case, and instead
103  * introduced rules for using backslashes to quote characters.  Higher-level
104  * code should add backslashes to a string constant to ensure it is treated
105  * as a single token.
106  */
107 char *
108 pg_strtok(int *length)
109 {
110         char       *local_str;          /* working pointer to string */
111         char       *ret_str;            /* start of token to return */
112
113         local_str = pg_strtok_ptr;
114
115         while (*local_str == ' ' || *local_str == '\n' || *local_str == '\t')
116                 local_str++;
117
118         if (*local_str == '\0')
119         {
120                 *length = 0;
121                 pg_strtok_ptr = local_str;
122                 return NULL;                    /* no more tokens */
123         }
124
125         /*
126          * Now pointing at start of next token.
127          */
128         ret_str = local_str;
129
130         if (*local_str == '(' || *local_str == ')' ||
131                 *local_str == '{' || *local_str == '}')
132         {
133                 /* special 1-character token */
134                 local_str++;
135         }
136         else
137         {
138                 /* Normal token, possibly containing backslashes */
139                 while (*local_str != '\0' &&
140                            *local_str != ' ' && *local_str != '\n' &&
141                            *local_str != '\t' &&
142                            *local_str != '(' && *local_str != ')' &&
143                            *local_str != '{' && *local_str != '}')
144                 {
145                         if (*local_str == '\\' && local_str[1] != '\0')
146                                 local_str += 2;
147                         else
148                                 local_str++;
149                 }
150         }
151
152         *length = local_str - ret_str;
153
154         /* Recognize special case for "empty" token */
155         if (*length == 2 && ret_str[0] == '<' && ret_str[1] == '>')
156                 *length = 0;
157
158         pg_strtok_ptr = local_str;
159
160         return ret_str;
161 }
162
163 /*
164  * debackslash -
165  *        create a palloc'd string holding the given token.
166  *        any protective backslashes in the token are removed.
167  */
168 char *
169 debackslash(char *token, int length)
170 {
171         char       *result = palloc(length + 1);
172         char       *ptr = result;
173
174         while (length > 0)
175         {
176                 if (*token == '\\' && length > 1)
177                         token++, length--;
178                 *ptr++ = *token++;
179                 length--;
180         }
181         *ptr = '\0';
182         return result;
183 }
184
185 #define RIGHT_PAREN (1000000 + 1)
186 #define LEFT_PAREN      (1000000 + 2)
187 #define NODE_SYM        (1000000 + 3)
188 #define AT_SYMBOL       (1000000 + 4)
189 #define ATOM_TOKEN      (1000000 + 5)
190
191 /*
192  * nodeTokenType -
193  *        returns the type of the node token contained in token.
194  *        It returns one of the following valid NodeTags:
195  *              T_Integer, T_Float, T_String, T_BitString
196  *        and some of its own:
197  *              RIGHT_PAREN, LEFT_PAREN, NODE_SYM, AT_SYMBOL, ATOM_TOKEN
198  *
199  *        Assumption: the ascii representation is legal
200  */
201 static NodeTag
202 nodeTokenType(char *token, int length)
203 {
204         NodeTag         retval;
205         char       *numptr;
206         int                     numlen;
207
208         /*
209          * Check if the token is a number
210          */
211         numptr = token;
212         numlen = length;
213         if (*numptr == '+' || *numptr == '-')
214                 numptr++, numlen--;
215         if ((numlen > 0 && isdigit((unsigned char) *numptr)) ||
216         (numlen > 1 && *numptr == '.' && isdigit((unsigned char) numptr[1])))
217         {
218                 /*
219                  * Yes.  Figure out whether it is integral or float; this requires
220                  * both a syntax check and a range check. strtol() can do both for
221                  * us. We know the token will end at a character that strtol will
222                  * stop at, so we do not need to modify the string.
223                  */
224                 long            val;
225                 char       *endptr;
226
227                 errno = 0;
228                 val = strtol(token, &endptr, 10);
229                 if (endptr != token + length || errno == ERANGE
230 #ifdef HAVE_LONG_INT_64
231                 /* if long > 32 bits, check for overflow of int4 */
232                         || val != (long) ((int32) val)
233 #endif
234                         )
235                         return T_Float;
236                 return T_Integer;
237         }
238
239         /*
240          * these three cases do not need length checks, since pg_strtok() will
241          * always treat them as single-byte tokens
242          */
243         else if (*token == '(')
244                 retval = LEFT_PAREN;
245         else if (*token == ')')
246                 retval = RIGHT_PAREN;
247         else if (*token == '{')
248                 retval = NODE_SYM;
249         else if (*token == '@' && length == 1)
250                 retval = AT_SYMBOL;
251         else if (*token == '\"' && length > 1 && token[length - 1] == '\"')
252                 retval = T_String;
253         else if (*token == 'b')
254                 retval = T_BitString;
255         else
256                 retval = ATOM_TOKEN;
257         return retval;
258 }
259
260 /*
261  * nodeRead -
262  *        Slightly higher-level reader.
263  *
264  * This routine applies some semantic knowledge on top of the purely
265  * lexical tokenizer pg_strtok().       It can read
266  *      * Value token nodes (integers, floats, or strings);
267  *      * General nodes (via parseNodeString() from readfuncs.c);
268  *      * Lists of the above.
269  *
270  * We assume pg_strtok is already initialized with a string to read (hence
271  * this should only be invoked from within a stringToNode operation).
272  * Any callers should set read_car_only to true.
273  */
274 void *
275 nodeRead(bool read_car_only)
276 {
277         char       *token;
278         int                     tok_len;
279         NodeTag         type;
280         Node       *this_value,
281                            *return_value;
282         bool            make_dotted_pair_cell = false;
283
284         token = pg_strtok(&tok_len);
285
286         if (token == NULL)
287                 return NULL;
288
289         type = nodeTokenType(token, tok_len);
290
291         switch (type)
292         {
293                 case NODE_SYM:
294                         this_value = parseNodeString();
295                         token = pg_strtok(&tok_len);
296                         if (token == NULL || token[0] != '}')
297                                 elog(ERROR, "did not find '}' at end of input node");
298                         if (!read_car_only)
299                                 make_dotted_pair_cell = true;
300                         else
301                                 make_dotted_pair_cell = false;
302                         break;
303                 case LEFT_PAREN:
304                         if (!read_car_only)
305                         {
306                                 List       *l = makeNode(List);
307
308                                 lfirst(l) = nodeRead(false);
309                                 lnext(l) = nodeRead(false);
310                                 this_value = (Node *) l;
311                         }
312                         else
313                                 this_value = nodeRead(false);
314                         break;
315                 case RIGHT_PAREN:
316                         this_value = NULL;
317                         break;
318                 case AT_SYMBOL:
319                         this_value = NULL;
320                         break;
321                 case ATOM_TOKEN:
322                         if (tok_len == 0)
323                         {
324                                 /* must be "<>" */
325                                 this_value = NULL;
326
327                                 /*
328                                  * It might be NULL but it is an atom!
329                                  */
330                                 if (read_car_only)
331                                         make_dotted_pair_cell = false;
332                                 else
333                                         make_dotted_pair_cell = true;
334                         }
335                         else
336                         {
337                                 /* !attention! result is not a Node.  Use with caution. */
338                                 this_value = (Node *) debackslash(token, tok_len);
339                                 make_dotted_pair_cell = true;
340                         }
341                         break;
342                 case T_Integer:
343
344                         /*
345                          * we know that the token terminates on a char atol will stop
346                          * at
347                          */
348                         this_value = (Node *) makeInteger(atol(token));
349                         make_dotted_pair_cell = true;
350                         break;
351                 case T_Float:
352                         {
353                                 char       *fval = (char *) palloc(tok_len + 1);
354
355                                 memcpy(fval, token, tok_len);
356                                 fval[tok_len] = '\0';
357                                 this_value = (Node *) makeFloat(fval);
358                                 make_dotted_pair_cell = true;
359                         }
360                         break;
361                 case T_String:
362                         /* need to remove leading and trailing quotes, and backslashes */
363                         this_value = (Node *) makeString(debackslash(token + 1, tok_len - 2));
364                         make_dotted_pair_cell = true;
365                         break;
366                 case T_BitString:
367                         {
368                                 char       *val = palloc(tok_len);
369
370                                 /* skip leading 'b' */
371                                 strncpy(val, token + 1, tok_len - 1);
372                                 val[tok_len - 1] = '\0';
373                                 this_value = (Node *) makeBitString(val);
374                                 break;
375                         }
376                 default:
377                         elog(ERROR, "unrecognized node type: %d", (int) type);
378                         this_value = NULL;      /* keep compiler happy */
379                         break;
380         }
381         if (make_dotted_pair_cell)
382         {
383                 List       *l = makeNode(List);
384
385                 lfirst(l) = this_value;
386
387                 if (!read_car_only)
388                         lnext(l) = nodeRead(false);
389                 else
390                         lnext(l) = NULL;
391                 return_value = (Node *) l;
392         }
393         else
394                 return_value = this_value;
395         return return_value;
396 }