Coverage Report

Created: 2026-09-28 06:55

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