/src/postgres/src/backend/nodes/readfuncs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * readfuncs.c |
4 | | * Reader functions for Postgres tree nodes. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/nodes/readfuncs.c |
12 | | * |
13 | | * NOTES |
14 | | * Parse location fields are written out by outfuncs.c, but only for |
15 | | * debugging use. When reading a location field, we normally discard |
16 | | * the stored value and set the location field to -1 (ie, "unknown"). |
17 | | * This is because nodes coming from a stored rule should not be thought |
18 | | * to have a known location in the current query's text. |
19 | | * |
20 | | * However, if restore_location_fields is true, we do restore location |
21 | | * fields from the string. This is currently intended only for use by the |
22 | | * debug_write_read_parse_plan_trees test code, which doesn't want to cause |
23 | | * any change in the node contents. |
24 | | * |
25 | | *------------------------------------------------------------------------- |
26 | | */ |
27 | | #include "postgres.h" |
28 | | |
29 | | #include <math.h> |
30 | | |
31 | | #include "miscadmin.h" |
32 | | #include "nodes/bitmapset.h" |
33 | | #include "nodes/readfuncs.h" |
34 | | |
35 | | |
36 | | /* |
37 | | * Macros to simplify reading of different kinds of fields. Use these |
38 | | * wherever possible to reduce the chance for silly typos. Note that these |
39 | | * hard-wire conventions about the names of the local variables in a Read |
40 | | * routine. |
41 | | */ |
42 | | |
43 | | /* Macros for declaring appropriate local variables */ |
44 | | |
45 | | /* A few guys need only local_node */ |
46 | | #define READ_LOCALS_NO_FIELDS(nodeTypeName) \ |
47 | 0 | nodeTypeName *local_node = makeNode(nodeTypeName) |
48 | | |
49 | | /* And a few guys need only the pg_strtok support fields */ |
50 | | #define READ_TEMP_LOCALS() \ |
51 | 0 | const char *token; \ |
52 | 0 | int length |
53 | | |
54 | | /* ... but most need both */ |
55 | | #define READ_LOCALS(nodeTypeName) \ |
56 | 0 | READ_LOCALS_NO_FIELDS(nodeTypeName); \ |
57 | 0 | READ_TEMP_LOCALS() |
58 | | |
59 | | /* Read an integer field (anything written as ":fldname %d") */ |
60 | | #define READ_INT_FIELD(fldname) \ |
61 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
62 | 0 | token = pg_strtok(&length); /* get field value */ \ |
63 | 0 | local_node->fldname = atoi(token) |
64 | | |
65 | | /* Read an unsigned integer field (anything written as ":fldname %u") */ |
66 | | #define READ_UINT_FIELD(fldname) \ |
67 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
68 | 0 | token = pg_strtok(&length); /* get field value */ \ |
69 | 0 | local_node->fldname = atoui(token) |
70 | | |
71 | | /* Read a signed integer field (anything written using INT64_FORMAT) */ |
72 | | #define READ_INT64_FIELD(fldname) \ |
73 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
74 | 0 | token = pg_strtok(&length); /* get field value */ \ |
75 | 0 | local_node->fldname = strtoi64(token, NULL, 10) |
76 | | |
77 | | /* Read an unsigned integer field (anything written using UINT64_FORMAT) */ |
78 | | #define READ_UINT64_FIELD(fldname) \ |
79 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
80 | 0 | token = pg_strtok(&length); /* get field value */ \ |
81 | 0 | local_node->fldname = strtou64(token, NULL, 10) |
82 | | |
83 | | /* Read a long integer field (anything written as ":fldname %ld") */ |
84 | | #define READ_LONG_FIELD(fldname) \ |
85 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
86 | 0 | token = pg_strtok(&length); /* get field value */ \ |
87 | 0 | local_node->fldname = atol(token) |
88 | | |
89 | | /* Read an OID field (don't hard-wire assumption that OID is same as uint) */ |
90 | | #define READ_OID_FIELD(fldname) \ |
91 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
92 | 0 | token = pg_strtok(&length); /* get field value */ \ |
93 | 0 | local_node->fldname = atooid(token) |
94 | | |
95 | | /* Read a char field (ie, one ascii character) */ |
96 | | #define READ_CHAR_FIELD(fldname) \ |
97 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
98 | 0 | token = pg_strtok(&length); /* get field value */ \ |
99 | 0 | /* avoid overhead of calling debackslash() for one char */ \ |
100 | 0 | local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0]) |
101 | | |
102 | | /* Read an enumerated-type field that was written as an integer code */ |
103 | | #define READ_ENUM_FIELD(fldname, enumtype) \ |
104 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
105 | 0 | token = pg_strtok(&length); /* get field value */ \ |
106 | 0 | local_node->fldname = (enumtype) atoi(token) |
107 | | |
108 | | /* Read a float field */ |
109 | | #define READ_FLOAT_FIELD(fldname) \ |
110 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
111 | 0 | token = pg_strtok(&length); /* get field value */ \ |
112 | 0 | local_node->fldname = atof(token) |
113 | | |
114 | | /* Read a boolean field */ |
115 | | #define READ_BOOL_FIELD(fldname) \ |
116 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
117 | 0 | token = pg_strtok(&length); /* get field value */ \ |
118 | 0 | local_node->fldname = strtobool(token) |
119 | | |
120 | | /* Read a character-string field */ |
121 | | #define READ_STRING_FIELD(fldname) \ |
122 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
123 | 0 | token = pg_strtok(&length); /* get field value */ \ |
124 | 0 | local_node->fldname = nullable_string(token, length) |
125 | | |
126 | | /* Read a parse location field (and possibly throw away the value) */ |
127 | | #ifdef DEBUG_NODE_TESTS_ENABLED |
128 | | #define READ_LOCATION_FIELD(fldname) \ |
129 | | token = pg_strtok(&length); /* skip :fldname */ \ |
130 | | token = pg_strtok(&length); /* get field value */ \ |
131 | | local_node->fldname = restore_location_fields ? atoi(token) : -1 |
132 | | #else |
133 | | #define READ_LOCATION_FIELD(fldname) \ |
134 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
135 | 0 | token = pg_strtok(&length); /* get field value */ \ |
136 | 0 | (void) token; /* in case not used elsewhere */ \ |
137 | 0 | local_node->fldname = -1 /* set field to "unknown" */ |
138 | | #endif |
139 | | |
140 | | /* Read a Node field */ |
141 | | #define READ_NODE_FIELD(fldname) \ |
142 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
143 | 0 | (void) token; /* in case not used elsewhere */ \ |
144 | 0 | local_node->fldname = nodeRead(NULL, 0) |
145 | | |
146 | | /* Read a bitmapset field */ |
147 | | #define READ_BITMAPSET_FIELD(fldname) \ |
148 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
149 | 0 | (void) token; /* in case not used elsewhere */ \ |
150 | 0 | local_node->fldname = _readBitmapset() |
151 | | |
152 | | /* Read an attribute number array */ |
153 | | #define READ_ATTRNUMBER_ARRAY(fldname, len) \ |
154 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
155 | 0 | local_node->fldname = readAttrNumberCols(len) |
156 | | |
157 | | /* Read an oid array */ |
158 | | #define READ_OID_ARRAY(fldname, len) \ |
159 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
160 | 0 | local_node->fldname = readOidCols(len) |
161 | | |
162 | | /* Read an int array */ |
163 | | #define READ_INT_ARRAY(fldname, len) \ |
164 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
165 | 0 | local_node->fldname = readIntCols(len) |
166 | | |
167 | | /* Read a bool array */ |
168 | | #define READ_BOOL_ARRAY(fldname, len) \ |
169 | 0 | token = pg_strtok(&length); /* skip :fldname */ \ |
170 | 0 | local_node->fldname = readBoolCols(len) |
171 | | |
172 | | /* Routine exit */ |
173 | | #define READ_DONE() \ |
174 | 0 | return local_node |
175 | | |
176 | | |
177 | | /* |
178 | | * NOTE: use atoi() to read values written with %d, or atoui() to read |
179 | | * values written with %u in outfuncs.c. An exception is OID values, |
180 | | * for which use atooid(). (As of 7.1, outfuncs.c writes OIDs as %u, |
181 | | * but this will probably change in the future.) |
182 | | */ |
183 | 0 | #define atoui(x) ((unsigned int) strtoul((x), NULL, 10)) |
184 | | |
185 | 0 | #define strtobool(x) ((*(x) == 't') ? true : false) |
186 | | |
187 | | static char * |
188 | | nullable_string(const char *token, int length) |
189 | 0 | { |
190 | | /* outToken emits <> for NULL, and pg_strtok makes that an empty string */ |
191 | 0 | if (length == 0) |
192 | 0 | return NULL; |
193 | | /* outToken emits "" for empty string */ |
194 | 0 | if (length == 2 && token[0] == '"' && token[1] == '"') |
195 | 0 | return pstrdup(""); |
196 | | /* otherwise, we must remove protective backslashes added by outToken */ |
197 | 0 | return debackslash(token, length); |
198 | 0 | } |
199 | | |
200 | | |
201 | | /* |
202 | | * _readBitmapset |
203 | | * |
204 | | * Note: this code is used in contexts where we know that a Bitmapset |
205 | | * is expected. There is equivalent code in nodeRead() that can read a |
206 | | * Bitmapset when we come across one in other contexts. |
207 | | */ |
208 | | static Bitmapset * |
209 | | _readBitmapset(void) |
210 | 0 | { |
211 | 0 | Bitmapset *result = NULL; |
212 | |
|
213 | 0 | READ_TEMP_LOCALS(); |
214 | |
|
215 | 0 | token = pg_strtok(&length); |
216 | 0 | if (token == NULL) |
217 | 0 | elog(ERROR, "incomplete Bitmapset structure"); |
218 | 0 | if (length != 1 || token[0] != '(') |
219 | 0 | elog(ERROR, "unrecognized token: \"%.*s\"", length, token); |
220 | | |
221 | 0 | token = pg_strtok(&length); |
222 | 0 | if (token == NULL) |
223 | 0 | elog(ERROR, "incomplete Bitmapset structure"); |
224 | 0 | if (length != 1 || token[0] != 'b') |
225 | 0 | elog(ERROR, "unrecognized token: \"%.*s\"", length, token); |
226 | | |
227 | 0 | for (;;) |
228 | 0 | { |
229 | 0 | int val; |
230 | 0 | char *endptr; |
231 | |
|
232 | 0 | token = pg_strtok(&length); |
233 | 0 | if (token == NULL) |
234 | 0 | elog(ERROR, "unterminated Bitmapset structure"); |
235 | 0 | if (length == 1 && token[0] == ')') |
236 | 0 | break; |
237 | 0 | val = (int) strtol(token, &endptr, 10); |
238 | 0 | if (endptr != token + length) |
239 | 0 | elog(ERROR, "unrecognized integer: \"%.*s\"", length, token); |
240 | 0 | result = bms_add_member(result, val); |
241 | 0 | } |
242 | | |
243 | 0 | return result; |
244 | 0 | } |
245 | | |
246 | | /* |
247 | | * We export this function for use by extensions that define extensible nodes. |
248 | | * That's somewhat historical, though, because calling nodeRead() will work. |
249 | | */ |
250 | | Bitmapset * |
251 | | readBitmapset(void) |
252 | 0 | { |
253 | 0 | return _readBitmapset(); |
254 | 0 | } |
255 | | |
256 | | #include "readfuncs.funcs.c" |
257 | | |
258 | | |
259 | | /* |
260 | | * Support functions for nodes with custom_read_write attribute or |
261 | | * special_read_write attribute |
262 | | */ |
263 | | |
264 | | static Const * |
265 | | _readConst(void) |
266 | 0 | { |
267 | 0 | READ_LOCALS(Const); |
268 | |
|
269 | 0 | READ_OID_FIELD(consttype); |
270 | 0 | READ_INT_FIELD(consttypmod); |
271 | 0 | READ_OID_FIELD(constcollid); |
272 | 0 | READ_INT_FIELD(constlen); |
273 | 0 | READ_BOOL_FIELD(constbyval); |
274 | 0 | READ_BOOL_FIELD(constisnull); |
275 | 0 | READ_LOCATION_FIELD(location); |
276 | |
|
277 | 0 | token = pg_strtok(&length); /* skip :constvalue */ |
278 | 0 | if (local_node->constisnull) |
279 | 0 | token = pg_strtok(&length); /* skip "<>" */ |
280 | 0 | else |
281 | 0 | local_node->constvalue = readDatum(local_node->constbyval); |
282 | |
|
283 | 0 | READ_DONE(); |
284 | 0 | } |
285 | | |
286 | | static BoolExpr * |
287 | | _readBoolExpr(void) |
288 | 0 | { |
289 | 0 | READ_LOCALS(BoolExpr); |
290 | | |
291 | | /* do-it-yourself enum representation */ |
292 | 0 | token = pg_strtok(&length); /* skip :boolop */ |
293 | 0 | token = pg_strtok(&length); /* get field value */ |
294 | 0 | if (length == 3 && strncmp(token, "and", 3) == 0) |
295 | 0 | local_node->boolop = AND_EXPR; |
296 | 0 | else if (length == 2 && strncmp(token, "or", 2) == 0) |
297 | 0 | local_node->boolop = OR_EXPR; |
298 | 0 | else if (length == 3 && strncmp(token, "not", 3) == 0) |
299 | 0 | local_node->boolop = NOT_EXPR; |
300 | 0 | else |
301 | 0 | elog(ERROR, "unrecognized boolop \"%.*s\"", length, token); |
302 | | |
303 | 0 | READ_NODE_FIELD(args); |
304 | 0 | READ_LOCATION_FIELD(location); |
305 | |
|
306 | 0 | READ_DONE(); |
307 | 0 | } |
308 | | |
309 | | static A_Const * |
310 | | _readA_Const(void) |
311 | 0 | { |
312 | 0 | READ_LOCALS(A_Const); |
313 | | |
314 | | /* We expect either NULL or :val here */ |
315 | 0 | token = pg_strtok(&length); |
316 | 0 | if (length == 4 && strncmp(token, "NULL", 4) == 0) |
317 | 0 | local_node->isnull = true; |
318 | 0 | else |
319 | 0 | { |
320 | 0 | union ValUnion *tmp = nodeRead(NULL, 0); |
321 | | |
322 | | /* To forestall valgrind complaints, copy only the valid data */ |
323 | 0 | switch (nodeTag(tmp)) |
324 | 0 | { |
325 | 0 | case T_Integer: |
326 | 0 | memcpy(&local_node->val, tmp, sizeof(Integer)); |
327 | 0 | break; |
328 | 0 | case T_Float: |
329 | 0 | memcpy(&local_node->val, tmp, sizeof(Float)); |
330 | 0 | break; |
331 | 0 | case T_Boolean: |
332 | 0 | memcpy(&local_node->val, tmp, sizeof(Boolean)); |
333 | 0 | break; |
334 | 0 | case T_String: |
335 | 0 | memcpy(&local_node->val, tmp, sizeof(String)); |
336 | 0 | break; |
337 | 0 | case T_BitString: |
338 | 0 | memcpy(&local_node->val, tmp, sizeof(BitString)); |
339 | 0 | break; |
340 | 0 | default: |
341 | 0 | elog(ERROR, "unrecognized node type: %d", |
342 | 0 | (int) nodeTag(tmp)); |
343 | 0 | break; |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | 0 | READ_LOCATION_FIELD(location); |
348 | |
|
349 | 0 | READ_DONE(); |
350 | 0 | } |
351 | | |
352 | | static RangeTblEntry * |
353 | | _readRangeTblEntry(void) |
354 | 0 | { |
355 | 0 | READ_LOCALS(RangeTblEntry); |
356 | |
|
357 | 0 | READ_NODE_FIELD(alias); |
358 | 0 | READ_NODE_FIELD(eref); |
359 | 0 | READ_ENUM_FIELD(rtekind, RTEKind); |
360 | |
|
361 | 0 | switch (local_node->rtekind) |
362 | 0 | { |
363 | 0 | case RTE_RELATION: |
364 | 0 | READ_OID_FIELD(relid); |
365 | 0 | READ_BOOL_FIELD(inh); |
366 | 0 | READ_CHAR_FIELD(relkind); |
367 | 0 | READ_INT_FIELD(rellockmode); |
368 | 0 | READ_UINT_FIELD(perminfoindex); |
369 | 0 | READ_NODE_FIELD(tablesample); |
370 | 0 | break; |
371 | 0 | case RTE_SUBQUERY: |
372 | 0 | READ_NODE_FIELD(subquery); |
373 | 0 | READ_BOOL_FIELD(security_barrier); |
374 | | /* we re-use these RELATION fields, too: */ |
375 | 0 | READ_OID_FIELD(relid); |
376 | 0 | READ_BOOL_FIELD(inh); |
377 | 0 | READ_CHAR_FIELD(relkind); |
378 | 0 | READ_INT_FIELD(rellockmode); |
379 | 0 | READ_UINT_FIELD(perminfoindex); |
380 | 0 | break; |
381 | 0 | case RTE_JOIN: |
382 | 0 | READ_ENUM_FIELD(jointype, JoinType); |
383 | 0 | READ_INT_FIELD(joinmergedcols); |
384 | 0 | READ_NODE_FIELD(joinaliasvars); |
385 | 0 | READ_NODE_FIELD(joinleftcols); |
386 | 0 | READ_NODE_FIELD(joinrightcols); |
387 | 0 | READ_NODE_FIELD(join_using_alias); |
388 | 0 | break; |
389 | 0 | case RTE_FUNCTION: |
390 | 0 | READ_NODE_FIELD(functions); |
391 | 0 | READ_BOOL_FIELD(funcordinality); |
392 | 0 | break; |
393 | 0 | case RTE_TABLEFUNC: |
394 | 0 | READ_NODE_FIELD(tablefunc); |
395 | | /* The RTE must have a copy of the column type info, if any */ |
396 | 0 | if (local_node->tablefunc) |
397 | 0 | { |
398 | 0 | TableFunc *tf = local_node->tablefunc; |
399 | |
|
400 | 0 | local_node->coltypes = tf->coltypes; |
401 | 0 | local_node->coltypmods = tf->coltypmods; |
402 | 0 | local_node->colcollations = tf->colcollations; |
403 | 0 | } |
404 | 0 | break; |
405 | 0 | case RTE_VALUES: |
406 | 0 | READ_NODE_FIELD(values_lists); |
407 | 0 | READ_NODE_FIELD(coltypes); |
408 | 0 | READ_NODE_FIELD(coltypmods); |
409 | 0 | READ_NODE_FIELD(colcollations); |
410 | 0 | break; |
411 | 0 | case RTE_CTE: |
412 | 0 | READ_STRING_FIELD(ctename); |
413 | 0 | READ_UINT_FIELD(ctelevelsup); |
414 | 0 | READ_BOOL_FIELD(self_reference); |
415 | 0 | READ_NODE_FIELD(coltypes); |
416 | 0 | READ_NODE_FIELD(coltypmods); |
417 | 0 | READ_NODE_FIELD(colcollations); |
418 | 0 | break; |
419 | 0 | case RTE_NAMEDTUPLESTORE: |
420 | 0 | READ_STRING_FIELD(enrname); |
421 | 0 | READ_FLOAT_FIELD(enrtuples); |
422 | 0 | READ_NODE_FIELD(coltypes); |
423 | 0 | READ_NODE_FIELD(coltypmods); |
424 | 0 | READ_NODE_FIELD(colcollations); |
425 | | /* we re-use these RELATION fields, too: */ |
426 | 0 | READ_OID_FIELD(relid); |
427 | 0 | break; |
428 | 0 | case RTE_RESULT: |
429 | | /* no extra fields */ |
430 | 0 | break; |
431 | 0 | case RTE_GROUP: |
432 | 0 | READ_NODE_FIELD(groupexprs); |
433 | 0 | break; |
434 | 0 | default: |
435 | 0 | elog(ERROR, "unrecognized RTE kind: %d", |
436 | 0 | (int) local_node->rtekind); |
437 | 0 | break; |
438 | 0 | } |
439 | | |
440 | 0 | READ_BOOL_FIELD(lateral); |
441 | 0 | READ_BOOL_FIELD(inFromCl); |
442 | 0 | READ_NODE_FIELD(securityQuals); |
443 | |
|
444 | 0 | READ_DONE(); |
445 | 0 | } |
446 | | |
447 | | static A_Expr * |
448 | | _readA_Expr(void) |
449 | 0 | { |
450 | 0 | READ_LOCALS(A_Expr); |
451 | |
|
452 | 0 | token = pg_strtok(&length); |
453 | |
|
454 | 0 | if (length == 3 && strncmp(token, "ANY", 3) == 0) |
455 | 0 | { |
456 | 0 | local_node->kind = AEXPR_OP_ANY; |
457 | 0 | READ_NODE_FIELD(name); |
458 | 0 | } |
459 | 0 | else if (length == 3 && strncmp(token, "ALL", 3) == 0) |
460 | 0 | { |
461 | 0 | local_node->kind = AEXPR_OP_ALL; |
462 | 0 | READ_NODE_FIELD(name); |
463 | 0 | } |
464 | 0 | else if (length == 8 && strncmp(token, "DISTINCT", 8) == 0) |
465 | 0 | { |
466 | 0 | local_node->kind = AEXPR_DISTINCT; |
467 | 0 | READ_NODE_FIELD(name); |
468 | 0 | } |
469 | 0 | else if (length == 12 && strncmp(token, "NOT_DISTINCT", 12) == 0) |
470 | 0 | { |
471 | 0 | local_node->kind = AEXPR_NOT_DISTINCT; |
472 | 0 | READ_NODE_FIELD(name); |
473 | 0 | } |
474 | 0 | else if (length == 6 && strncmp(token, "NULLIF", 6) == 0) |
475 | 0 | { |
476 | 0 | local_node->kind = AEXPR_NULLIF; |
477 | 0 | READ_NODE_FIELD(name); |
478 | 0 | } |
479 | 0 | else if (length == 2 && strncmp(token, "IN", 2) == 0) |
480 | 0 | { |
481 | 0 | local_node->kind = AEXPR_IN; |
482 | 0 | READ_NODE_FIELD(name); |
483 | 0 | } |
484 | 0 | else if (length == 4 && strncmp(token, "LIKE", 4) == 0) |
485 | 0 | { |
486 | 0 | local_node->kind = AEXPR_LIKE; |
487 | 0 | READ_NODE_FIELD(name); |
488 | 0 | } |
489 | 0 | else if (length == 5 && strncmp(token, "ILIKE", 5) == 0) |
490 | 0 | { |
491 | 0 | local_node->kind = AEXPR_ILIKE; |
492 | 0 | READ_NODE_FIELD(name); |
493 | 0 | } |
494 | 0 | else if (length == 7 && strncmp(token, "SIMILAR", 7) == 0) |
495 | 0 | { |
496 | 0 | local_node->kind = AEXPR_SIMILAR; |
497 | 0 | READ_NODE_FIELD(name); |
498 | 0 | } |
499 | 0 | else if (length == 7 && strncmp(token, "BETWEEN", 7) == 0) |
500 | 0 | { |
501 | 0 | local_node->kind = AEXPR_BETWEEN; |
502 | 0 | READ_NODE_FIELD(name); |
503 | 0 | } |
504 | 0 | else if (length == 11 && strncmp(token, "NOT_BETWEEN", 11) == 0) |
505 | 0 | { |
506 | 0 | local_node->kind = AEXPR_NOT_BETWEEN; |
507 | 0 | READ_NODE_FIELD(name); |
508 | 0 | } |
509 | 0 | else if (length == 11 && strncmp(token, "BETWEEN_SYM", 11) == 0) |
510 | 0 | { |
511 | 0 | local_node->kind = AEXPR_BETWEEN_SYM; |
512 | 0 | READ_NODE_FIELD(name); |
513 | 0 | } |
514 | 0 | else if (length == 15 && strncmp(token, "NOT_BETWEEN_SYM", 15) == 0) |
515 | 0 | { |
516 | 0 | local_node->kind = AEXPR_NOT_BETWEEN_SYM; |
517 | 0 | READ_NODE_FIELD(name); |
518 | 0 | } |
519 | 0 | else if (length == 5 && strncmp(token, ":name", 5) == 0) |
520 | 0 | { |
521 | 0 | local_node->kind = AEXPR_OP; |
522 | 0 | local_node->name = nodeRead(NULL, 0); |
523 | 0 | } |
524 | 0 | else |
525 | 0 | elog(ERROR, "unrecognized A_Expr kind: \"%.*s\"", length, token); |
526 | | |
527 | 0 | READ_NODE_FIELD(lexpr); |
528 | 0 | READ_NODE_FIELD(rexpr); |
529 | 0 | READ_LOCATION_FIELD(rexpr_list_start); |
530 | 0 | READ_LOCATION_FIELD(rexpr_list_end); |
531 | 0 | READ_LOCATION_FIELD(location); |
532 | |
|
533 | 0 | READ_DONE(); |
534 | 0 | } |
535 | | |
536 | | static ExtensibleNode * |
537 | | _readExtensibleNode(void) |
538 | 0 | { |
539 | 0 | const ExtensibleNodeMethods *methods; |
540 | 0 | ExtensibleNode *local_node; |
541 | 0 | const char *extnodename; |
542 | |
|
543 | 0 | READ_TEMP_LOCALS(); |
544 | |
|
545 | 0 | token = pg_strtok(&length); /* skip :extnodename */ |
546 | 0 | token = pg_strtok(&length); /* get extnodename */ |
547 | |
|
548 | 0 | extnodename = nullable_string(token, length); |
549 | 0 | if (!extnodename) |
550 | 0 | elog(ERROR, "extnodename has to be supplied"); |
551 | 0 | methods = GetExtensibleNodeMethods(extnodename, false); |
552 | |
|
553 | 0 | local_node = (ExtensibleNode *) newNode(methods->node_size, |
554 | 0 | T_ExtensibleNode); |
555 | 0 | local_node->extnodename = extnodename; |
556 | | |
557 | | /* deserialize the private fields */ |
558 | 0 | methods->nodeRead(local_node); |
559 | |
|
560 | 0 | READ_DONE(); |
561 | 0 | } |
562 | | |
563 | | |
564 | | /* |
565 | | * parseNodeString |
566 | | * |
567 | | * Given a character string representing a node tree, parseNodeString creates |
568 | | * the internal node structure. |
569 | | * |
570 | | * The string to be read must already have been loaded into pg_strtok(). |
571 | | */ |
572 | | Node * |
573 | | parseNodeString(void) |
574 | 0 | { |
575 | 0 | READ_TEMP_LOCALS(); |
576 | | |
577 | | /* Guard against stack overflow due to overly complex expressions */ |
578 | 0 | check_stack_depth(); |
579 | |
|
580 | 0 | token = pg_strtok(&length); |
581 | |
|
582 | 0 | #define MATCH(tokname, namelen) \ |
583 | 0 | (length == namelen && memcmp(token, tokname, namelen) == 0) |
584 | |
|
585 | 0 | #include "readfuncs.switch.c" |
586 | | |
587 | 0 | elog(ERROR, "badly formatted node string \"%.32s\"...", token); |
588 | 0 | return NULL; /* keep compiler quiet */ |
589 | 0 | } |
590 | | |
591 | | |
592 | | /* |
593 | | * readDatum |
594 | | * |
595 | | * Given a string representation of a constant, recreate the appropriate |
596 | | * Datum. The string representation embeds length info, but not byValue, |
597 | | * so we must be told that. |
598 | | */ |
599 | | Datum |
600 | | readDatum(bool typbyval) |
601 | 0 | { |
602 | 0 | Size length, |
603 | 0 | i; |
604 | 0 | int tokenLength; |
605 | 0 | const char *token; |
606 | 0 | Datum res; |
607 | 0 | char *s; |
608 | | |
609 | | /* |
610 | | * read the actual length of the value |
611 | | */ |
612 | 0 | token = pg_strtok(&tokenLength); |
613 | 0 | length = atoui(token); |
614 | |
|
615 | 0 | token = pg_strtok(&tokenLength); /* read the '[' */ |
616 | 0 | if (token == NULL || token[0] != '[') |
617 | 0 | elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu", |
618 | 0 | token ? token : "[NULL]", length); |
619 | | |
620 | 0 | if (typbyval) |
621 | 0 | { |
622 | 0 | if (length > (Size) sizeof(Datum)) |
623 | 0 | elog(ERROR, "byval datum but length = %zu", length); |
624 | 0 | res = (Datum) 0; |
625 | 0 | s = (char *) (&res); |
626 | 0 | for (i = 0; i < (Size) sizeof(Datum); i++) |
627 | 0 | { |
628 | 0 | token = pg_strtok(&tokenLength); |
629 | 0 | s[i] = (char) atoi(token); |
630 | 0 | } |
631 | 0 | } |
632 | 0 | else if (length <= 0) |
633 | 0 | res = (Datum) NULL; |
634 | 0 | else |
635 | 0 | { |
636 | 0 | s = (char *) palloc(length); |
637 | 0 | for (i = 0; i < length; i++) |
638 | 0 | { |
639 | 0 | token = pg_strtok(&tokenLength); |
640 | 0 | s[i] = (char) atoi(token); |
641 | 0 | } |
642 | 0 | res = PointerGetDatum(s); |
643 | 0 | } |
644 | | |
645 | 0 | token = pg_strtok(&tokenLength); /* read the ']' */ |
646 | 0 | if (token == NULL || token[0] != ']') |
647 | 0 | elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu", |
648 | 0 | token ? token : "[NULL]", length); |
649 | | |
650 | 0 | return res; |
651 | 0 | } |
652 | | |
653 | | /* |
654 | | * common implementation for scalar-array-reading functions |
655 | | * |
656 | | * The data format is either "<>" for a NULL pointer (in which case numCols |
657 | | * is ignored) or "(item item item)" where the number of items must equal |
658 | | * numCols. The convfunc must be okay with stopping at whitespace or a |
659 | | * right parenthesis, since pg_strtok won't null-terminate the token. |
660 | | */ |
661 | | #define READ_SCALAR_ARRAY(fnname, datatype, convfunc) \ |
662 | | datatype * \ |
663 | 0 | fnname(int numCols) \ |
664 | 0 | { \ |
665 | 0 | datatype *vals; \ |
666 | 0 | READ_TEMP_LOCALS(); \ |
667 | 0 | token = pg_strtok(&length); \ |
668 | 0 | if (token == NULL) \ |
669 | 0 | elog(ERROR, "incomplete scalar array"); \ |
670 | 0 | if (length == 0) \ |
671 | 0 | return NULL; /* it was "<>", so return NULL pointer */ \ |
672 | 0 | if (length != 1 || token[0] != '(') \ |
673 | 0 | elog(ERROR, "unrecognized token: \"%.*s\"", length, token); \ |
674 | 0 | vals = (datatype *) palloc(numCols * sizeof(datatype)); \ |
675 | 0 | for (int i = 0; i < numCols; i++) \ |
676 | 0 | { \ |
677 | 0 | token = pg_strtok(&length); \ |
678 | 0 | if (token == NULL || token[0] == ')') \ |
679 | 0 | elog(ERROR, "incomplete scalar array"); \ |
680 | 0 | vals[i] = convfunc(token); \ |
681 | 0 | } \ |
682 | 0 | token = pg_strtok(&length); \ |
683 | 0 | if (token == NULL || length != 1 || token[0] != ')') \ |
684 | 0 | elog(ERROR, "incomplete scalar array"); \ |
685 | 0 | return vals; \ |
686 | 0 | } Unexecuted instantiation: readAttrNumberCols Unexecuted instantiation: readOidCols Unexecuted instantiation: readIntCols Unexecuted instantiation: readBoolCols |
687 | | |
688 | | /* |
689 | | * Note: these functions are exported in nodes.h for possible use by |
690 | | * extensions, so don't mess too much with their names or API. |
691 | | */ |
692 | | READ_SCALAR_ARRAY(readAttrNumberCols, int16, atoi) |
693 | 0 | READ_SCALAR_ARRAY(readOidCols, Oid, atooid) |
694 | | /* outfuncs.c has writeIndexCols, but we don't yet need that here */ |
695 | | /* READ_SCALAR_ARRAY(readIndexCols, Index, atoui) */ |
696 | | READ_SCALAR_ARRAY(readIntCols, int, atoi) |
697 | 0 | READ_SCALAR_ARRAY(readBoolCols, bool, strtobool) |