Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/backend/nodes/print.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * print.c
4
 *    various print routines (used mostly for debugging)
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/print.c
12
 *
13
 * HISTORY
14
 *    AUTHOR      DATE      MAJOR EVENT
15
 *    Andrew Yu     Oct 26, 1994  file creation
16
 *
17
 *-------------------------------------------------------------------------
18
 */
19
20
#include "postgres.h"
21
22
#include "access/printtup.h"
23
#include "lib/stringinfo.h"
24
#include "nodes/nodeFuncs.h"
25
#include "nodes/pathnodes.h"
26
#include "nodes/print.h"
27
#include "parser/parsetree.h"
28
#include "utils/lsyscache.h"
29
30
31
/*
32
 * print
33
 *    print contents of Node to stdout
34
 */
35
void
36
print(const void *obj)
37
0
{
38
0
  char     *s;
39
0
  char     *f;
40
41
0
  s = nodeToStringWithLocations(obj);
42
0
  f = format_node_dump(s);
43
0
  pfree(s);
44
0
  printf("%s\n", f);
45
0
  fflush(stdout);
46
0
  pfree(f);
47
0
}
48
49
/*
50
 * pprint
51
 *    pretty-print contents of Node to stdout
52
 */
53
void
54
pprint(const void *obj)
55
0
{
56
0
  char     *s;
57
0
  char     *f;
58
59
0
  s = nodeToStringWithLocations(obj);
60
0
  f = pretty_format_node_dump(s);
61
0
  pfree(s);
62
0
  printf("%s\n", f);
63
0
  fflush(stdout);
64
0
  pfree(f);
65
0
}
66
67
/*
68
 * elog_node_display
69
 *    send pretty-printed contents of Node to postmaster log
70
 */
71
void
72
elog_node_display(int lev, const char *title, const void *obj, bool pretty)
73
0
{
74
0
  char     *s;
75
0
  char     *f;
76
77
0
  s = nodeToStringWithLocations(obj);
78
0
  if (pretty)
79
0
    f = pretty_format_node_dump(s);
80
0
  else
81
0
    f = format_node_dump(s);
82
0
  pfree(s);
83
0
  ereport(lev,
84
0
      (errmsg_internal("%s:", title),
85
0
       errdetail_internal("%s", f)));
86
0
  pfree(f);
87
0
}
88
89
/*
90
 * Format a nodeToString output for display on a terminal.
91
 *
92
 * The result is a palloc'd string.
93
 *
94
 * This version just tries to break at whitespace.
95
 */
96
char *
97
format_node_dump(const char *dump)
98
0
{
99
0
#define LINELEN   78
100
0
  char    line[LINELEN + 1];
101
0
  StringInfoData str;
102
0
  int     i;
103
0
  int     j;
104
0
  int     k;
105
106
0
  initStringInfo(&str);
107
0
  i = 0;
108
0
  for (;;)
109
0
  {
110
0
    for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++)
111
0
      line[j] = dump[i];
112
0
    if (dump[i] == '\0')
113
0
      break;
114
0
    if (dump[i] == ' ')
115
0
    {
116
      /* ok to break at adjacent space */
117
0
      i++;
118
0
    }
119
0
    else
120
0
    {
121
0
      for (k = j - 1; k > 0; k--)
122
0
        if (line[k] == ' ')
123
0
          break;
124
0
      if (k > 0)
125
0
      {
126
        /* back up; will reprint all after space */
127
0
        i -= (j - k - 1);
128
0
        j = k;
129
0
      }
130
0
    }
131
0
    line[j] = '\0';
132
0
    appendStringInfo(&str, "%s\n", line);
133
0
  }
134
0
  if (j > 0)
135
0
  {
136
0
    line[j] = '\0';
137
0
    appendStringInfo(&str, "%s\n", line);
138
0
  }
139
0
  return str.data;
140
0
#undef LINELEN
141
0
}
142
143
/*
144
 * Format a nodeToString output for display on a terminal.
145
 *
146
 * The result is a palloc'd string.
147
 *
148
 * This version tries to indent intelligently.
149
 */
150
char *
151
pretty_format_node_dump(const char *dump)
152
0
{
153
0
#define INDENTSTOP  3
154
0
#define MAXINDENT 60
155
0
#define LINELEN   78
156
0
  char    line[LINELEN + 1];
157
0
  StringInfoData str;
158
0
  int     indentLev;
159
0
  int     indentDist;
160
0
  int     i;
161
0
  int     j;
162
163
0
  initStringInfo(&str);
164
0
  indentLev = 0;        /* logical indent level */
165
0
  indentDist = 0;       /* physical indent distance */
166
0
  i = 0;
167
0
  for (;;)
168
0
  {
169
0
    for (j = 0; j < indentDist; j++)
170
0
      line[j] = ' ';
171
0
    for (; j < LINELEN && dump[i] != '\0'; i++, j++)
172
0
    {
173
0
      line[j] = dump[i];
174
0
      switch (line[j])
175
0
      {
176
0
        case '}':
177
0
          if (j != indentDist)
178
0
          {
179
            /* print data before the } */
180
0
            line[j] = '\0';
181
0
            appendStringInfo(&str, "%s\n", line);
182
0
          }
183
          /* print the } at indentDist */
184
0
          line[indentDist] = '}';
185
0
          line[indentDist + 1] = '\0';
186
0
          appendStringInfo(&str, "%s\n", line);
187
          /* outdent */
188
0
          if (indentLev > 0)
189
0
          {
190
0
            indentLev--;
191
0
            indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
192
0
          }
193
0
          j = indentDist - 1;
194
          /* j will equal indentDist on next loop iteration */
195
          /* suppress whitespace just after } */
196
0
          while (dump[i + 1] == ' ')
197
0
            i++;
198
0
          break;
199
0
        case ')':
200
          /* force line break after ), unless another ) follows */
201
0
          if (dump[i + 1] != ')')
202
0
          {
203
0
            line[j + 1] = '\0';
204
0
            appendStringInfo(&str, "%s\n", line);
205
0
            j = indentDist - 1;
206
0
            while (dump[i + 1] == ' ')
207
0
              i++;
208
0
          }
209
0
          break;
210
0
        case '{':
211
          /* force line break before { */
212
0
          if (j != indentDist)
213
0
          {
214
0
            line[j] = '\0';
215
0
            appendStringInfo(&str, "%s\n", line);
216
0
          }
217
          /* indent */
218
0
          indentLev++;
219
0
          indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
220
0
          for (j = 0; j < indentDist; j++)
221
0
            line[j] = ' ';
222
0
          line[j] = dump[i];
223
0
          break;
224
0
        case ':':
225
          /* force line break before : */
226
0
          if (j != indentDist)
227
0
          {
228
0
            line[j] = '\0';
229
0
            appendStringInfo(&str, "%s\n", line);
230
0
          }
231
0
          j = indentDist;
232
0
          line[j] = dump[i];
233
0
          break;
234
0
      }
235
0
    }
236
0
    line[j] = '\0';
237
0
    if (dump[i] == '\0')
238
0
      break;
239
0
    appendStringInfo(&str, "%s\n", line);
240
0
  }
241
0
  if (j > 0)
242
0
    appendStringInfo(&str, "%s\n", line);
243
0
  return str.data;
244
0
#undef INDENTSTOP
245
0
#undef MAXINDENT
246
0
#undef LINELEN
247
0
}
248
249
/*
250
 * print_rt
251
 *    print contents of range table
252
 */
253
void
254
print_rt(const List *rtable)
255
0
{
256
0
  const ListCell *l;
257
0
  int     i = 1;
258
259
0
  printf("resno\trefname  \trelid\tinFromCl\n");
260
0
  printf("-----\t---------\t-----\t--------\n");
261
0
  foreach(l, rtable)
262
0
  {
263
0
    RangeTblEntry *rte = lfirst(l);
264
265
0
    switch (rte->rtekind)
266
0
    {
267
0
      case RTE_RELATION:
268
0
        printf("%d\t%s\t%u\t%c",
269
0
             i, rte->eref->aliasname, rte->relid, rte->relkind);
270
0
        break;
271
0
      case RTE_SUBQUERY:
272
0
        printf("%d\t%s\t[subquery]",
273
0
             i, rte->eref->aliasname);
274
0
        break;
275
0
      case RTE_JOIN:
276
0
        printf("%d\t%s\t[join]",
277
0
             i, rte->eref->aliasname);
278
0
        break;
279
0
      case RTE_FUNCTION:
280
0
        printf("%d\t%s\t[rangefunction]",
281
0
             i, rte->eref->aliasname);
282
0
        break;
283
0
      case RTE_TABLEFUNC:
284
0
        printf("%d\t%s\t[table function]",
285
0
             i, rte->eref->aliasname);
286
0
        break;
287
0
      case RTE_VALUES:
288
0
        printf("%d\t%s\t[values list]",
289
0
             i, rte->eref->aliasname);
290
0
        break;
291
0
      case RTE_CTE:
292
0
        printf("%d\t%s\t[cte]",
293
0
             i, rte->eref->aliasname);
294
0
        break;
295
0
      case RTE_NAMEDTUPLESTORE:
296
0
        printf("%d\t%s\t[tuplestore]",
297
0
             i, rte->eref->aliasname);
298
0
        break;
299
0
      case RTE_RESULT:
300
0
        printf("%d\t%s\t[result]",
301
0
             i, rte->eref->aliasname);
302
0
        break;
303
0
      case RTE_GROUP:
304
0
        printf("%d\t%s\t[group]",
305
0
             i, rte->eref->aliasname);
306
0
        break;
307
0
      default:
308
0
        printf("%d\t%s\t[unknown rtekind]",
309
0
             i, rte->eref->aliasname);
310
0
    }
311
312
0
    printf("\t%s\t%s\n",
313
0
         (rte->inh ? "inh" : ""),
314
0
         (rte->inFromCl ? "inFromCl" : ""));
315
0
    i++;
316
0
  }
317
0
}
318
319
320
/*
321
 * print_expr
322
 *    print an expression
323
 */
324
void
325
print_expr(const Node *expr, const List *rtable)
326
0
{
327
0
  if (expr == NULL)
328
0
  {
329
0
    printf("<>");
330
0
    return;
331
0
  }
332
333
0
  if (IsA(expr, Var))
334
0
  {
335
0
    const Var  *var = (const Var *) expr;
336
0
    char     *relname,
337
0
           *attname;
338
339
0
    switch (var->varno)
340
0
    {
341
0
      case INNER_VAR:
342
0
        relname = "INNER";
343
0
        attname = "?";
344
0
        break;
345
0
      case OUTER_VAR:
346
0
        relname = "OUTER";
347
0
        attname = "?";
348
0
        break;
349
0
      case INDEX_VAR:
350
0
        relname = "INDEX";
351
0
        attname = "?";
352
0
        break;
353
0
      default:
354
0
        {
355
0
          RangeTblEntry *rte;
356
357
0
          Assert(var->varno > 0 &&
358
0
               (int) var->varno <= list_length(rtable));
359
0
          rte = rt_fetch(var->varno, rtable);
360
0
          relname = rte->eref->aliasname;
361
0
          attname = get_rte_attribute_name(rte, var->varattno);
362
0
        }
363
0
        break;
364
0
    }
365
0
    printf("%s.%s", relname, attname);
366
0
  }
367
0
  else if (IsA(expr, Const))
368
0
  {
369
0
    const Const *c = (const Const *) expr;
370
0
    Oid     typoutput;
371
0
    bool    typIsVarlena;
372
0
    char     *outputstr;
373
374
0
    if (c->constisnull)
375
0
    {
376
0
      printf("NULL");
377
0
      return;
378
0
    }
379
380
0
    getTypeOutputInfo(c->consttype,
381
0
              &typoutput, &typIsVarlena);
382
383
0
    outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
384
0
    printf("%s", outputstr);
385
0
    pfree(outputstr);
386
0
  }
387
0
  else if (IsA(expr, OpExpr))
388
0
  {
389
0
    const OpExpr *e = (const OpExpr *) expr;
390
0
    char     *opname;
391
392
0
    opname = get_opname(e->opno);
393
0
    if (list_length(e->args) > 1)
394
0
    {
395
0
      print_expr(get_leftop((const Expr *) e), rtable);
396
0
      printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
397
0
      print_expr(get_rightop((const Expr *) e), rtable);
398
0
    }
399
0
    else
400
0
    {
401
0
      printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
402
0
      print_expr(get_leftop((const Expr *) e), rtable);
403
0
    }
404
0
  }
405
0
  else if (IsA(expr, FuncExpr))
406
0
  {
407
0
    const FuncExpr *e = (const FuncExpr *) expr;
408
0
    char     *funcname;
409
0
    ListCell   *l;
410
411
0
    funcname = get_func_name(e->funcid);
412
0
    printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
413
0
    foreach(l, e->args)
414
0
    {
415
0
      print_expr(lfirst(l), rtable);
416
0
      if (lnext(e->args, l))
417
0
        printf(",");
418
0
    }
419
0
    printf(")");
420
0
  }
421
0
  else
422
0
    printf("unknown expr");
423
0
}
424
425
/*
426
 * print_pathkeys -
427
 *    pathkeys list of PathKeys
428
 */
429
void
430
print_pathkeys(const List *pathkeys, const List *rtable)
431
0
{
432
0
  const ListCell *i;
433
434
0
  printf("(");
435
0
  foreach(i, pathkeys)
436
0
  {
437
0
    PathKey    *pathkey = (PathKey *) lfirst(i);
438
0
    EquivalenceClass *eclass;
439
0
    ListCell   *k;
440
0
    bool    first = true;
441
442
0
    eclass = pathkey->pk_eclass;
443
    /* chase up, in case pathkey is non-canonical */
444
0
    while (eclass->ec_merged)
445
0
      eclass = eclass->ec_merged;
446
447
0
    printf("(");
448
0
    foreach(k, eclass->ec_members)
449
0
    {
450
0
      EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
451
452
0
      if (first)
453
0
        first = false;
454
0
      else
455
0
        printf(", ");
456
0
      print_expr((Node *) mem->em_expr, rtable);
457
0
    }
458
0
    printf(")");
459
0
    if (lnext(pathkeys, i))
460
0
      printf(", ");
461
0
  }
462
0
  printf(")\n");
463
0
}
464
465
/*
466
 * print_tl
467
 *    print targetlist in a more legible way.
468
 */
469
void
470
print_tl(const List *tlist, const List *rtable)
471
0
{
472
0
  const ListCell *tl;
473
474
0
  printf("(\n");
475
0
  foreach(tl, tlist)
476
0
  {
477
0
    TargetEntry *tle = (TargetEntry *) lfirst(tl);
478
479
0
    printf("\t%d %s\t", tle->resno,
480
0
         tle->resname ? tle->resname : "<null>");
481
0
    if (tle->ressortgroupref != 0)
482
0
      printf("(%u):\t", tle->ressortgroupref);
483
0
    else
484
0
      printf("    :\t");
485
0
    print_expr((Node *) tle->expr, rtable);
486
0
    printf("\n");
487
0
  }
488
0
  printf(")\n");
489
0
}
490
491
/*
492
 * print_slot
493
 *    print out the tuple with the given TupleTableSlot
494
 */
495
void
496
print_slot(TupleTableSlot *slot)
497
0
{
498
0
  if (TupIsNull(slot))
499
0
  {
500
0
    printf("tuple is null.\n");
501
0
    return;
502
0
  }
503
0
  if (!slot->tts_tupleDescriptor)
504
0
  {
505
0
    printf("no tuple descriptor.\n");
506
0
    return;
507
0
  }
508
509
0
  debugtup(slot, NULL);
510
0
}