Coverage Report

Created: 2025-10-09 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/nodes/outfuncs.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * outfuncs.c
4
 *    Output 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/outfuncs.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include <ctype.h>
18
19
#include "access/attnum.h"
20
#include "common/shortest_dec.h"
21
#include "lib/stringinfo.h"
22
#include "miscadmin.h"
23
#include "nodes/bitmapset.h"
24
#include "nodes/nodes.h"
25
#include "nodes/pg_list.h"
26
#include "utils/datum.h"
27
28
/* State flag that determines how nodeToStringInternal() should treat location fields */
29
static bool write_location_fields = false;
30
31
static void outChar(StringInfo str, char c);
32
static void outDouble(StringInfo str, double d);
33
34
35
/*
36
 * Macros to simplify output of different kinds of fields.  Use these
37
 * wherever possible to reduce the chance for silly typos.  Note that these
38
 * hard-wire conventions about the names of the local variables in an Out
39
 * routine.
40
 */
41
42
/* Write the label for the node type */
43
#define WRITE_NODE_TYPE(nodelabel) \
44
0
  appendStringInfoString(str, nodelabel)
45
46
/* Write an integer field (anything written as ":fldname %d") */
47
#define WRITE_INT_FIELD(fldname) \
48
0
  appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname)
49
50
/* Write an unsigned integer field (anything written as ":fldname %u") */
51
#define WRITE_UINT_FIELD(fldname) \
52
0
  appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
53
54
/* Write a signed integer field (anything written with INT64_FORMAT) */
55
#define WRITE_INT64_FIELD(fldname) \
56
0
  appendStringInfo(str, \
57
0
           " :" CppAsString(fldname) " " INT64_FORMAT, \
58
0
           node->fldname)
59
60
/* Write an unsigned integer field (anything written with UINT64_FORMAT) */
61
#define WRITE_UINT64_FIELD(fldname) \
62
0
  appendStringInfo(str, " :" CppAsString(fldname) " " UINT64_FORMAT, \
63
0
           node->fldname)
64
65
/* Write an OID field (don't hard-wire assumption that OID is same as uint) */
66
#define WRITE_OID_FIELD(fldname) \
67
0
  appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
68
69
/* Write a long-integer field */
70
#define WRITE_LONG_FIELD(fldname) \
71
0
  appendStringInfo(str, " :" CppAsString(fldname) " %ld", node->fldname)
72
73
/* Write a char field (ie, one ascii character) */
74
#define WRITE_CHAR_FIELD(fldname) \
75
0
  (appendStringInfo(str, " :" CppAsString(fldname) " "), \
76
0
   outChar(str, node->fldname))
77
78
/* Write an enumerated-type field as an integer code */
79
#define WRITE_ENUM_FIELD(fldname, enumtype) \
80
0
  appendStringInfo(str, " :" CppAsString(fldname) " %d", \
81
0
           (int) node->fldname)
82
83
/* Write a float field (actually, they're double) */
84
#define WRITE_FLOAT_FIELD(fldname) \
85
0
  (appendStringInfo(str, " :" CppAsString(fldname) " "), \
86
0
   outDouble(str, node->fldname))
87
88
/* Write a boolean field */
89
#define WRITE_BOOL_FIELD(fldname) \
90
0
  appendStringInfo(str, " :" CppAsString(fldname) " %s", \
91
0
           booltostr(node->fldname))
92
93
/* Write a character-string (possibly NULL) field */
94
#define WRITE_STRING_FIELD(fldname) \
95
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
96
0
   outToken(str, node->fldname))
97
98
/* Write a parse location field (actually same as INT case) */
99
#define WRITE_LOCATION_FIELD(fldname) \
100
0
  appendStringInfo(str, " :" CppAsString(fldname) " %d", write_location_fields ? node->fldname : -1)
101
102
/* Write a Node field */
103
#define WRITE_NODE_FIELD(fldname) \
104
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
105
0
   outNode(str, node->fldname))
106
107
/* Write a bitmapset field */
108
#define WRITE_BITMAPSET_FIELD(fldname) \
109
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
110
0
   outBitmapset(str, node->fldname))
111
112
/* Write a variable-length array (not a List) of Node pointers */
113
#define WRITE_NODE_ARRAY(fldname, len) \
114
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
115
0
   writeNodeArray(str, (const Node * const *) node->fldname, len))
116
117
/* Write a variable-length array of AttrNumber */
118
#define WRITE_ATTRNUMBER_ARRAY(fldname, len) \
119
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
120
0
   writeAttrNumberCols(str, node->fldname, len))
121
122
/* Write a variable-length array of Oid */
123
#define WRITE_OID_ARRAY(fldname, len) \
124
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
125
0
   writeOidCols(str, node->fldname, len))
126
127
/* Write a variable-length array of Index */
128
#define WRITE_INDEX_ARRAY(fldname, len) \
129
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
130
0
   writeIndexCols(str, node->fldname, len))
131
132
/* Write a variable-length array of int */
133
#define WRITE_INT_ARRAY(fldname, len) \
134
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
135
0
   writeIntCols(str, node->fldname, len))
136
137
/* Write a variable-length array of bool */
138
#define WRITE_BOOL_ARRAY(fldname, len) \
139
0
  (appendStringInfoString(str, " :" CppAsString(fldname) " "), \
140
0
   writeBoolCols(str, node->fldname, len))
141
142
0
#define booltostr(x)  ((x) ? "true" : "false")
143
144
145
/*
146
 * outToken
147
 *    Convert an ordinary string (eg, an identifier) into a form that
148
 *    will be decoded back to a plain token by read.c's functions.
149
 *
150
 *    If a null string pointer is given, it is encoded as '<>'.
151
 *    An empty string is encoded as '""'.  To avoid ambiguity, input
152
 *    strings beginning with '<' or '"' receive a leading backslash.
153
 */
154
void
155
outToken(StringInfo str, const char *s)
156
0
{
157
0
  if (s == NULL)
158
0
  {
159
0
    appendStringInfoString(str, "<>");
160
0
    return;
161
0
  }
162
0
  if (*s == '\0')
163
0
  {
164
0
    appendStringInfoString(str, "\"\"");
165
0
    return;
166
0
  }
167
168
  /*
169
   * Look for characters or patterns that are treated specially by read.c
170
   * (either in pg_strtok() or in nodeRead()), and therefore need a
171
   * protective backslash.
172
   */
173
  /* These characters only need to be quoted at the start of the string */
174
0
  if (*s == '<' ||
175
0
    *s == '"' ||
176
0
    isdigit((unsigned char) *s) ||
177
0
    ((*s == '+' || *s == '-') &&
178
0
     (isdigit((unsigned char) s[1]) || s[1] == '.')))
179
0
    appendStringInfoChar(str, '\\');
180
0
  while (*s)
181
0
  {
182
    /* These chars must be backslashed anywhere in the string */
183
0
    if (*s == ' ' || *s == '\n' || *s == '\t' ||
184
0
      *s == '(' || *s == ')' || *s == '{' || *s == '}' ||
185
0
      *s == '\\')
186
0
      appendStringInfoChar(str, '\\');
187
0
    appendStringInfoChar(str, *s++);
188
0
  }
189
0
}
190
191
/*
192
 * Convert one char.  Goes through outToken() so that special characters are
193
 * escaped.
194
 */
195
static void
196
outChar(StringInfo str, char c)
197
0
{
198
0
  char    in[2];
199
200
  /* Traditionally, we've represented \0 as <>, so keep doing that */
201
0
  if (c == '\0')
202
0
  {
203
0
    appendStringInfoString(str, "<>");
204
0
    return;
205
0
  }
206
207
0
  in[0] = c;
208
0
  in[1] = '\0';
209
210
0
  outToken(str, in);
211
0
}
212
213
/*
214
 * Convert a double value, attempting to ensure the value is preserved exactly.
215
 */
216
static void
217
outDouble(StringInfo str, double d)
218
0
{
219
0
  char    buf[DOUBLE_SHORTEST_DECIMAL_LEN];
220
221
0
  double_to_shortest_decimal_buf(d, buf);
222
0
  appendStringInfoString(str, buf);
223
0
}
224
225
/*
226
 * common implementation for scalar-array-writing functions
227
 *
228
 * The data format is either "<>" for a NULL pointer or "(item item item)".
229
 * fmtstr must include a leading space, and the rest of it must produce
230
 * something that will be seen as a single simple token by pg_strtok().
231
 * convfunc can be empty, or the name of a conversion macro or function.
232
 */
233
#define WRITE_SCALAR_ARRAY(fnname, datatype, fmtstr, convfunc) \
234
static void \
235
0
fnname(StringInfo str, const datatype *arr, int len) \
236
0
{ \
237
0
  if (arr != NULL) \
238
0
  { \
239
0
    appendStringInfoChar(str, '('); \
240
0
    for (int i = 0; i < len; i++) \
241
0
      appendStringInfo(str, fmtstr, convfunc(arr[i])); \
242
0
    appendStringInfoChar(str, ')'); \
243
0
  } \
244
0
  else \
245
0
    appendStringInfoString(str, "<>"); \
246
0
}
247
248
0
WRITE_SCALAR_ARRAY(writeAttrNumberCols, AttrNumber, " %d",)
249
0
WRITE_SCALAR_ARRAY(writeOidCols, Oid, " %u",)
250
0
WRITE_SCALAR_ARRAY(writeIndexCols, Index, " %u",)
251
0
WRITE_SCALAR_ARRAY(writeIntCols, int, " %d",)
252
0
WRITE_SCALAR_ARRAY(writeBoolCols, bool, " %s", booltostr)
253
254
/*
255
 * Print an array (not a List) of Node pointers.
256
 *
257
 * The decoration is identical to that of scalar arrays, but we can't
258
 * quite use appendStringInfo() in the loop.
259
 */
260
static void
261
writeNodeArray(StringInfo str, const Node *const *arr, int len)
262
0
{
263
0
  if (arr != NULL)
264
0
  {
265
0
    appendStringInfoChar(str, '(');
266
0
    for (int i = 0; i < len; i++)
267
0
    {
268
0
      appendStringInfoChar(str, ' ');
269
0
      outNode(str, arr[i]);
270
0
    }
271
0
    appendStringInfoChar(str, ')');
272
0
  }
273
0
  else
274
0
    appendStringInfoString(str, "<>");
275
0
}
276
277
/*
278
 * Print a List.
279
 */
280
static void
281
_outList(StringInfo str, const List *node)
282
0
{
283
0
  const ListCell *lc;
284
285
0
  appendStringInfoChar(str, '(');
286
287
0
  if (IsA(node, IntList))
288
0
    appendStringInfoChar(str, 'i');
289
0
  else if (IsA(node, OidList))
290
0
    appendStringInfoChar(str, 'o');
291
0
  else if (IsA(node, XidList))
292
0
    appendStringInfoChar(str, 'x');
293
294
0
  foreach(lc, node)
295
0
  {
296
    /*
297
     * For the sake of backward compatibility, we emit a slightly
298
     * different whitespace format for lists of nodes vs. other types of
299
     * lists. XXX: is this necessary?
300
     */
301
0
    if (IsA(node, List))
302
0
    {
303
0
      outNode(str, lfirst(lc));
304
0
      if (lnext(node, lc))
305
0
        appendStringInfoChar(str, ' ');
306
0
    }
307
0
    else if (IsA(node, IntList))
308
0
      appendStringInfo(str, " %d", lfirst_int(lc));
309
0
    else if (IsA(node, OidList))
310
0
      appendStringInfo(str, " %u", lfirst_oid(lc));
311
0
    else if (IsA(node, XidList))
312
0
      appendStringInfo(str, " %u", lfirst_xid(lc));
313
0
    else
314
0
      elog(ERROR, "unrecognized list node type: %d",
315
0
         (int) node->type);
316
0
  }
317
318
0
  appendStringInfoChar(str, ')');
319
0
}
320
321
/*
322
 * outBitmapset -
323
 *     converts a bitmap set of integers
324
 *
325
 * Note: the output format is "(b int int ...)", similar to an integer List.
326
 *
327
 * We export this function for use by extensions that define extensible nodes.
328
 * That's somewhat historical, though, because calling outNode() will work.
329
 */
330
void
331
outBitmapset(StringInfo str, const Bitmapset *bms)
332
0
{
333
0
  int     x;
334
335
0
  appendStringInfoChar(str, '(');
336
0
  appendStringInfoChar(str, 'b');
337
0
  x = -1;
338
0
  while ((x = bms_next_member(bms, x)) >= 0)
339
0
    appendStringInfo(str, " %d", x);
340
0
  appendStringInfoChar(str, ')');
341
0
}
342
343
/*
344
 * Print the value of a Datum given its type.
345
 */
346
void
347
outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
348
0
{
349
0
  Size    length,
350
0
        i;
351
0
  char     *s;
352
353
0
  length = datumGetSize(value, typbyval, typlen);
354
355
0
  if (typbyval)
356
0
  {
357
0
    s = (char *) (&value);
358
0
    appendStringInfo(str, "%u [ ", (unsigned int) length);
359
0
    for (i = 0; i < (Size) sizeof(Datum); i++)
360
0
      appendStringInfo(str, "%d ", (int) (s[i]));
361
0
    appendStringInfoChar(str, ']');
362
0
  }
363
0
  else
364
0
  {
365
0
    s = (char *) DatumGetPointer(value);
366
0
    if (!s)
367
0
      appendStringInfoString(str, "0 [ ]");
368
0
    else
369
0
    {
370
0
      appendStringInfo(str, "%u [ ", (unsigned int) length);
371
0
      for (i = 0; i < length; i++)
372
0
        appendStringInfo(str, "%d ", (int) (s[i]));
373
0
      appendStringInfoChar(str, ']');
374
0
    }
375
0
  }
376
0
}
377
378
379
#include "outfuncs.funcs.c"
380
381
382
/*
383
 * Support functions for nodes with custom_read_write attribute or
384
 * special_read_write attribute
385
 */
386
387
static void
388
_outConst(StringInfo str, const Const *node)
389
0
{
390
0
  WRITE_NODE_TYPE("CONST");
391
392
0
  WRITE_OID_FIELD(consttype);
393
0
  WRITE_INT_FIELD(consttypmod);
394
0
  WRITE_OID_FIELD(constcollid);
395
0
  WRITE_INT_FIELD(constlen);
396
0
  WRITE_BOOL_FIELD(constbyval);
397
0
  WRITE_BOOL_FIELD(constisnull);
398
0
  WRITE_LOCATION_FIELD(location);
399
400
0
  appendStringInfoString(str, " :constvalue ");
401
0
  if (node->constisnull)
402
0
    appendStringInfoString(str, "<>");
403
0
  else
404
0
    outDatum(str, node->constvalue, node->constlen, node->constbyval);
405
0
}
406
407
static void
408
_outBoolExpr(StringInfo str, const BoolExpr *node)
409
0
{
410
0
  char     *opstr = NULL;
411
412
0
  WRITE_NODE_TYPE("BOOLEXPR");
413
414
  /* do-it-yourself enum representation */
415
0
  switch (node->boolop)
416
0
  {
417
0
    case AND_EXPR:
418
0
      opstr = "and";
419
0
      break;
420
0
    case OR_EXPR:
421
0
      opstr = "or";
422
0
      break;
423
0
    case NOT_EXPR:
424
0
      opstr = "not";
425
0
      break;
426
0
  }
427
0
  appendStringInfoString(str, " :boolop ");
428
0
  outToken(str, opstr);
429
430
0
  WRITE_NODE_FIELD(args);
431
0
  WRITE_LOCATION_FIELD(location);
432
0
}
433
434
static void
435
_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
436
0
{
437
0
  int     i;
438
439
0
  WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");
440
441
0
  WRITE_UINT_FIELD(con_relid);
442
0
  WRITE_UINT_FIELD(ref_relid);
443
0
  WRITE_INT_FIELD(nkeys);
444
0
  WRITE_ATTRNUMBER_ARRAY(conkey, node->nkeys);
445
0
  WRITE_ATTRNUMBER_ARRAY(confkey, node->nkeys);
446
0
  WRITE_OID_ARRAY(conpfeqop, node->nkeys);
447
0
  WRITE_INT_FIELD(nmatched_ec);
448
0
  WRITE_INT_FIELD(nconst_ec);
449
0
  WRITE_INT_FIELD(nmatched_rcols);
450
0
  WRITE_INT_FIELD(nmatched_ri);
451
  /* for compactness, just print the number of matches per column: */
452
0
  appendStringInfoString(str, " :eclass");
453
0
  for (i = 0; i < node->nkeys; i++)
454
0
    appendStringInfo(str, " %d", (node->eclass[i] != NULL));
455
0
  appendStringInfoString(str, " :rinfos");
456
0
  for (i = 0; i < node->nkeys; i++)
457
0
    appendStringInfo(str, " %d", list_length(node->rinfos[i]));
458
0
}
459
460
static void
461
_outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
462
0
{
463
  /*
464
   * To simplify reading, we just chase up to the topmost merged EC and
465
   * print that, without bothering to show the merge-ees separately.
466
   */
467
0
  while (node->ec_merged)
468
0
    node = node->ec_merged;
469
470
0
  WRITE_NODE_TYPE("EQUIVALENCECLASS");
471
472
0
  WRITE_NODE_FIELD(ec_opfamilies);
473
0
  WRITE_OID_FIELD(ec_collation);
474
0
  WRITE_INT_FIELD(ec_childmembers_size);
475
0
  WRITE_NODE_FIELD(ec_members);
476
0
  WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size);
477
0
  WRITE_NODE_FIELD(ec_sources);
478
  /* Only ec_derives_list is written; hash is not serialized. */
479
0
  WRITE_NODE_FIELD(ec_derives_list);
480
0
  WRITE_BITMAPSET_FIELD(ec_relids);
481
0
  WRITE_BOOL_FIELD(ec_has_const);
482
0
  WRITE_BOOL_FIELD(ec_has_volatile);
483
0
  WRITE_BOOL_FIELD(ec_broken);
484
0
  WRITE_UINT_FIELD(ec_sortref);
485
0
  WRITE_UINT_FIELD(ec_min_security);
486
0
  WRITE_UINT_FIELD(ec_max_security);
487
0
}
488
489
static void
490
_outExtensibleNode(StringInfo str, const ExtensibleNode *node)
491
0
{
492
0
  const ExtensibleNodeMethods *methods;
493
494
0
  methods = GetExtensibleNodeMethods(node->extnodename, false);
495
496
0
  WRITE_NODE_TYPE("EXTENSIBLENODE");
497
498
0
  WRITE_STRING_FIELD(extnodename);
499
500
  /* serialize the private fields */
501
0
  methods->nodeOut(str, node);
502
0
}
503
504
static void
505
_outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
506
0
{
507
0
  WRITE_NODE_TYPE("RANGETBLENTRY");
508
509
0
  WRITE_NODE_FIELD(alias);
510
0
  WRITE_NODE_FIELD(eref);
511
0
  WRITE_ENUM_FIELD(rtekind, RTEKind);
512
513
0
  switch (node->rtekind)
514
0
  {
515
0
    case RTE_RELATION:
516
0
      WRITE_OID_FIELD(relid);
517
0
      WRITE_BOOL_FIELD(inh);
518
0
      WRITE_CHAR_FIELD(relkind);
519
0
      WRITE_INT_FIELD(rellockmode);
520
0
      WRITE_UINT_FIELD(perminfoindex);
521
0
      WRITE_NODE_FIELD(tablesample);
522
0
      break;
523
0
    case RTE_SUBQUERY:
524
0
      WRITE_NODE_FIELD(subquery);
525
0
      WRITE_BOOL_FIELD(security_barrier);
526
      /* we re-use these RELATION fields, too: */
527
0
      WRITE_OID_FIELD(relid);
528
0
      WRITE_BOOL_FIELD(inh);
529
0
      WRITE_CHAR_FIELD(relkind);
530
0
      WRITE_INT_FIELD(rellockmode);
531
0
      WRITE_UINT_FIELD(perminfoindex);
532
0
      break;
533
0
    case RTE_JOIN:
534
0
      WRITE_ENUM_FIELD(jointype, JoinType);
535
0
      WRITE_INT_FIELD(joinmergedcols);
536
0
      WRITE_NODE_FIELD(joinaliasvars);
537
0
      WRITE_NODE_FIELD(joinleftcols);
538
0
      WRITE_NODE_FIELD(joinrightcols);
539
0
      WRITE_NODE_FIELD(join_using_alias);
540
0
      break;
541
0
    case RTE_FUNCTION:
542
0
      WRITE_NODE_FIELD(functions);
543
0
      WRITE_BOOL_FIELD(funcordinality);
544
0
      break;
545
0
    case RTE_TABLEFUNC:
546
0
      WRITE_NODE_FIELD(tablefunc);
547
0
      break;
548
0
    case RTE_VALUES:
549
0
      WRITE_NODE_FIELD(values_lists);
550
0
      WRITE_NODE_FIELD(coltypes);
551
0
      WRITE_NODE_FIELD(coltypmods);
552
0
      WRITE_NODE_FIELD(colcollations);
553
0
      break;
554
0
    case RTE_CTE:
555
0
      WRITE_STRING_FIELD(ctename);
556
0
      WRITE_UINT_FIELD(ctelevelsup);
557
0
      WRITE_BOOL_FIELD(self_reference);
558
0
      WRITE_NODE_FIELD(coltypes);
559
0
      WRITE_NODE_FIELD(coltypmods);
560
0
      WRITE_NODE_FIELD(colcollations);
561
0
      break;
562
0
    case RTE_NAMEDTUPLESTORE:
563
0
      WRITE_STRING_FIELD(enrname);
564
0
      WRITE_FLOAT_FIELD(enrtuples);
565
0
      WRITE_NODE_FIELD(coltypes);
566
0
      WRITE_NODE_FIELD(coltypmods);
567
0
      WRITE_NODE_FIELD(colcollations);
568
      /* we re-use these RELATION fields, too: */
569
0
      WRITE_OID_FIELD(relid);
570
0
      break;
571
0
    case RTE_RESULT:
572
      /* no extra fields */
573
0
      break;
574
0
    case RTE_GROUP:
575
0
      WRITE_NODE_FIELD(groupexprs);
576
0
      break;
577
0
    default:
578
0
      elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
579
0
      break;
580
0
  }
581
582
0
  WRITE_BOOL_FIELD(lateral);
583
0
  WRITE_BOOL_FIELD(inFromCl);
584
0
  WRITE_NODE_FIELD(securityQuals);
585
0
}
586
587
static void
588
_outA_Expr(StringInfo str, const A_Expr *node)
589
0
{
590
0
  WRITE_NODE_TYPE("A_EXPR");
591
592
0
  switch (node->kind)
593
0
  {
594
0
    case AEXPR_OP:
595
0
      WRITE_NODE_FIELD(name);
596
0
      break;
597
0
    case AEXPR_OP_ANY:
598
0
      appendStringInfoString(str, " ANY");
599
0
      WRITE_NODE_FIELD(name);
600
0
      break;
601
0
    case AEXPR_OP_ALL:
602
0
      appendStringInfoString(str, " ALL");
603
0
      WRITE_NODE_FIELD(name);
604
0
      break;
605
0
    case AEXPR_DISTINCT:
606
0
      appendStringInfoString(str, " DISTINCT");
607
0
      WRITE_NODE_FIELD(name);
608
0
      break;
609
0
    case AEXPR_NOT_DISTINCT:
610
0
      appendStringInfoString(str, " NOT_DISTINCT");
611
0
      WRITE_NODE_FIELD(name);
612
0
      break;
613
0
    case AEXPR_NULLIF:
614
0
      appendStringInfoString(str, " NULLIF");
615
0
      WRITE_NODE_FIELD(name);
616
0
      break;
617
0
    case AEXPR_IN:
618
0
      appendStringInfoString(str, " IN");
619
0
      WRITE_NODE_FIELD(name);
620
0
      break;
621
0
    case AEXPR_LIKE:
622
0
      appendStringInfoString(str, " LIKE");
623
0
      WRITE_NODE_FIELD(name);
624
0
      break;
625
0
    case AEXPR_ILIKE:
626
0
      appendStringInfoString(str, " ILIKE");
627
0
      WRITE_NODE_FIELD(name);
628
0
      break;
629
0
    case AEXPR_SIMILAR:
630
0
      appendStringInfoString(str, " SIMILAR");
631
0
      WRITE_NODE_FIELD(name);
632
0
      break;
633
0
    case AEXPR_BETWEEN:
634
0
      appendStringInfoString(str, " BETWEEN");
635
0
      WRITE_NODE_FIELD(name);
636
0
      break;
637
0
    case AEXPR_NOT_BETWEEN:
638
0
      appendStringInfoString(str, " NOT_BETWEEN");
639
0
      WRITE_NODE_FIELD(name);
640
0
      break;
641
0
    case AEXPR_BETWEEN_SYM:
642
0
      appendStringInfoString(str, " BETWEEN_SYM");
643
0
      WRITE_NODE_FIELD(name);
644
0
      break;
645
0
    case AEXPR_NOT_BETWEEN_SYM:
646
0
      appendStringInfoString(str, " NOT_BETWEEN_SYM");
647
0
      WRITE_NODE_FIELD(name);
648
0
      break;
649
0
    default:
650
0
      elog(ERROR, "unrecognized A_Expr_Kind: %d", (int) node->kind);
651
0
      break;
652
0
  }
653
654
0
  WRITE_NODE_FIELD(lexpr);
655
0
  WRITE_NODE_FIELD(rexpr);
656
0
  WRITE_LOCATION_FIELD(rexpr_list_start);
657
0
  WRITE_LOCATION_FIELD(rexpr_list_end);
658
0
  WRITE_LOCATION_FIELD(location);
659
0
}
660
661
static void
662
_outInteger(StringInfo str, const Integer *node)
663
0
{
664
0
  appendStringInfo(str, "%d", node->ival);
665
0
}
666
667
static void
668
_outFloat(StringInfo str, const Float *node)
669
0
{
670
  /*
671
   * We assume the value is a valid numeric literal and so does not need
672
   * quoting.
673
   */
674
0
  appendStringInfoString(str, node->fval);
675
0
}
676
677
static void
678
_outBoolean(StringInfo str, const Boolean *node)
679
0
{
680
0
  appendStringInfoString(str, node->boolval ? "true" : "false");
681
0
}
682
683
static void
684
_outString(StringInfo str, const String *node)
685
0
{
686
  /*
687
   * We use outToken to provide escaping of the string's content, but we
688
   * don't want it to convert an empty string to '""', because we're putting
689
   * double quotes around the string already.
690
   */
691
0
  appendStringInfoChar(str, '"');
692
0
  if (node->sval[0] != '\0')
693
0
    outToken(str, node->sval);
694
0
  appendStringInfoChar(str, '"');
695
0
}
696
697
static void
698
_outBitString(StringInfo str, const BitString *node)
699
0
{
700
  /*
701
   * The lexer will always produce a string starting with 'b' or 'x'.  There
702
   * might be characters following that that need escaping, but outToken
703
   * won't escape the 'b' or 'x'.  This is relied on by nodeTokenType.
704
   */
705
0
  Assert(node->bsval[0] == 'b' || node->bsval[0] == 'x');
706
0
  outToken(str, node->bsval);
707
0
}
708
709
static void
710
_outA_Const(StringInfo str, const A_Const *node)
711
0
{
712
0
  WRITE_NODE_TYPE("A_CONST");
713
714
0
  if (node->isnull)
715
0
    appendStringInfoString(str, " NULL");
716
0
  else
717
0
  {
718
0
    appendStringInfoString(str, " :val ");
719
0
    outNode(str, &node->val);
720
0
  }
721
0
  WRITE_LOCATION_FIELD(location);
722
0
}
723
724
725
/*
726
 * outNode -
727
 *    converts a Node into ascii string and append it to 'str'
728
 */
729
void
730
outNode(StringInfo str, const void *obj)
731
{
732
  /* Guard against stack overflow due to overly complex expressions */
733
  check_stack_depth();
734
735
  if (obj == NULL)
736
    appendStringInfoString(str, "<>");
737
  else if (IsA(obj, List) || IsA(obj, IntList) || IsA(obj, OidList) ||
738
       IsA(obj, XidList))
739
    _outList(str, obj);
740
  /* nodeRead does not want to see { } around these! */
741
  else if (IsA(obj, Integer))
742
    _outInteger(str, (Integer *) obj);
743
  else if (IsA(obj, Float))
744
    _outFloat(str, (Float *) obj);
745
  else if (IsA(obj, Boolean))
746
    _outBoolean(str, (Boolean *) obj);
747
  else if (IsA(obj, String))
748
    _outString(str, (String *) obj);
749
  else if (IsA(obj, BitString))
750
    _outBitString(str, (BitString *) obj);
751
  else if (IsA(obj, Bitmapset))
752
    outBitmapset(str, (Bitmapset *) obj);
753
  else
754
  {
755
    appendStringInfoChar(str, '{');
756
    switch (nodeTag(obj))
757
    {
758
#include "outfuncs.switch.c"
759
760
      default:
761
762
        /*
763
         * This should be an ERROR, but it's too useful to be able to
764
         * dump structures that outNode only understands part of.
765
         */
766
        elog(WARNING, "could not dump unrecognized node type: %d",
767
           (int) nodeTag(obj));
768
        break;
769
    }
770
    appendStringInfoChar(str, '}');
771
  }
772
}
773
774
/*
775
 * nodeToString -
776
 *     returns the ascii representation of the Node as a palloc'd string
777
 *
778
 * write_loc_fields determines whether location fields are output with their
779
 * actual value rather than -1.  The actual value can be useful for debugging,
780
 * but for most uses, the actual value is not useful, since the original query
781
 * string is no longer available.
782
 */
783
static char *
784
nodeToStringInternal(const void *obj, bool write_loc_fields)
785
0
{
786
0
  StringInfoData str;
787
0
  bool    save_write_location_fields;
788
789
0
  save_write_location_fields = write_location_fields;
790
0
  write_location_fields = write_loc_fields;
791
792
  /* see stringinfo.h for an explanation of this maneuver */
793
0
  initStringInfo(&str);
794
0
  outNode(&str, obj);
795
796
0
  write_location_fields = save_write_location_fields;
797
798
0
  return str.data;
799
0
}
800
801
/*
802
 * Externally visible entry points
803
 */
804
char *
805
nodeToString(const void *obj)
806
0
{
807
0
  return nodeToStringInternal(obj, false);
808
0
}
809
810
char *
811
nodeToStringWithLocations(const void *obj)
812
0
{
813
0
  return nodeToStringInternal(obj, true);
814
0
}
815
816
817
/*
818
 * bmsToString -
819
 *     returns the ascii representation of the Bitmapset as a palloc'd string
820
 */
821
char *
822
bmsToString(const Bitmapset *bms)
823
0
{
824
0
  StringInfoData str;
825
826
  /* see stringinfo.h for an explanation of this maneuver */
827
0
  initStringInfo(&str);
828
0
  outBitmapset(&str, bms);
829
0
  return str.data;
830
0
}