~ubuntu-branches/ubuntu/lucid/postgresql-8.4/lucid-proposed

« back to all changes in this revision

Viewing changes to src/backend/nodes/read.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

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