Coverage Report

Created: 2025-07-03 06:49

/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)