1 /*-------------------------------------------------------------------------
4 * various print routines (used mostly for debugging)
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.68 2004/05/30 23:40:27 neilc Exp $
14 * AUTHOR DATE MAJOR EVENT
15 * Andrew Yu Oct 26, 1994 file creation
17 *-------------------------------------------------------------------------
22 #include "access/printtup.h"
23 #include "catalog/pg_type.h"
24 #include "lib/stringinfo.h"
25 #include "nodes/print.h"
26 #include "optimizer/clauses.h"
27 #include "parser/parsetree.h"
28 #include "utils/lsyscache.h"
29 #include "utils/syscache.h"
31 static char *plannode_type(Plan *p);
35 * print contents of Node to stdout
43 s = nodeToString(obj);
44 f = format_node_dump(s);
53 * pretty-print contents of Node to stdout
61 s = nodeToString(obj);
62 f = pretty_format_node_dump(s);
71 * send pretty-printed contents of Node to postmaster log
74 elog_node_display(int lev, const char *title, void *obj, bool pretty)
79 s = nodeToString(obj);
81 f = pretty_format_node_dump(s);
83 f = format_node_dump(s);
86 (errmsg_internal("%s:", title),
92 * Format a nodeToString output for display on a terminal.
94 * The result is a palloc'd string.
96 * This version just tries to break at whitespace.
99 format_node_dump(const char *dump)
102 char line[LINELEN + 1];
108 initStringInfo(&str);
112 for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++)
118 /* ok to break at adjacent space */
123 for (k = j - 1; k > 0; k--)
128 /* back up; will reprint all after space */
134 appendStringInfo(&str, "%s\n", line);
139 appendStringInfo(&str, "%s\n", line);
146 * Format a nodeToString output for display on a terminal.
148 * The result is a palloc'd string.
150 * This version tries to indent intelligently.
153 pretty_format_node_dump(const char *dump)
158 char line[LINELEN + 1];
165 initStringInfo(&str);
166 indentLev = 0; /* logical indent level */
167 indentDist = 0; /* physical indent distance */
171 for (j = 0; j < indentDist; j++)
173 for (; j < LINELEN && dump[i] != '\0'; i++, j++)
181 /* print data before the } */
183 appendStringInfo(&str, "%s\n", line);
185 /* print the } at indentDist */
186 line[indentDist] = '}';
187 line[indentDist + 1] = '\0';
188 appendStringInfo(&str, "%s\n", line);
193 indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
196 /* j will equal indentDist on next loop iteration */
197 /* suppress whitespace just after } */
198 while (dump[i+1] == ' ')
202 /* force line break after ), unless another ) follows */
203 if (dump[i+1] != ')')
206 appendStringInfo(&str, "%s\n", line);
208 while (dump[i+1] == ' ')
213 /* force line break before { */
217 appendStringInfo(&str, "%s\n", line);
221 indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
222 for (j = 0; j < indentDist; j++)
227 /* force line break before : */
231 appendStringInfo(&str, "%s\n", line);
241 appendStringInfo(&str, "%s\n", line);
244 appendStringInfo(&str, "%s\n", line);
253 * print contents of range table
256 print_rt(List *rtable)
261 printf("resno\trefname \trelid\tinFromCl\n");
262 printf("-----\t---------\t-----\t--------\n");
265 RangeTblEntry *rte = lfirst(l);
267 switch (rte->rtekind)
271 i, rte->eref->aliasname, rte->relid);
274 printf("%d\t%s\t[subquery]",
275 i, rte->eref->aliasname);
278 printf("%d\t%s\t[rangefunction]",
279 i, rte->eref->aliasname);
282 printf("%d\t%s\t[join]",
283 i, rte->eref->aliasname);
286 printf("%d\t%s\t[special]",
287 i, rte->eref->aliasname);
290 printf("%d\t%s\t[unknown rtekind]",
291 i, rte->eref->aliasname);
295 (rte->inh ? "inh" : ""),
296 (rte->inFromCl ? "inFromCl" : ""));
304 * print an expression
307 print_expr(Node *expr, List *rtable)
317 Var *var = (Var *) expr;
335 Assert(var->varno > 0 &&
336 (int) var->varno <= list_length(rtable));
337 rte = rt_fetch(var->varno, rtable);
338 relname = rte->eref->aliasname;
339 attname = get_rte_attribute_name(rte, var->varattno);
343 printf("%s.%s", relname, attname);
345 else if (IsA(expr, Const))
347 Const *c = (Const *) expr;
359 typeTup = SearchSysCache(TYPEOID,
360 ObjectIdGetDatum(c->consttype),
362 if (!HeapTupleIsValid(typeTup))
363 elog(ERROR, "cache lookup failed for type %u", c->consttype);
364 typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
365 typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
366 ReleaseSysCache(typeTup);
368 outputstr = DatumGetCString(OidFunctionCall3(typoutput,
370 ObjectIdGetDatum(typelem),
372 printf("%s", outputstr);
375 else if (IsA(expr, OpExpr))
377 OpExpr *e = (OpExpr *) expr;
380 opname = get_opname(e->opno);
381 if (list_length(e->args) > 1)
383 print_expr(get_leftop((Expr *) e), rtable);
384 printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
385 print_expr(get_rightop((Expr *) e), rtable);
389 /* we print prefix and postfix ops the same... */
390 printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
391 print_expr(get_leftop((Expr *) e), rtable);
394 else if (IsA(expr, FuncExpr))
396 FuncExpr *e = (FuncExpr *) expr;
400 funcname = get_func_name(e->funcid);
401 printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
404 print_expr(lfirst(l), rtable);
411 printf("unknown expr");
416 * pathkeys list of list of PathKeyItems
419 print_pathkeys(List *pathkeys, List *rtable)
426 List *pathkey = (List *) lfirst(i);
432 PathKeyItem *item = (PathKeyItem *) lfirst(k);
434 print_expr(item->key, rtable);
447 * print targetlist in a more legible way.
450 print_tl(List *tlist, List *rtable)
457 TargetEntry *tle = (TargetEntry *) lfirst(tl);
459 printf("\t%d %s\t", tle->resdom->resno,
460 tle->resdom->resname ? tle->resdom->resname : "<null>");
461 if (tle->resdom->ressortgroupref != 0)
462 printf("(%u):\t", tle->resdom->ressortgroupref);
465 print_expr((Node *) tle->expr, rtable);
473 * print out the tuple with the given TupleTableSlot
476 print_slot(TupleTableSlot *slot)
480 printf("tuple is null.\n");
483 if (!slot->ttc_tupleDescriptor)
485 printf("no tuple descriptor.\n");
489 debugtup(slot->val, slot->ttc_tupleDescriptor, NULL);
493 plannode_type(Plan *p)
512 return "SUBQUERYSCAN";
514 return "FUNCTIONSCAN";
545 * Recursively prints a simple text description of the plan tree
548 print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
551 char extraInfo[NAMEDATALEN + 100];
555 for (i = 0; i < indentLevel; i++)
557 printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
558 p->startup_cost, p->total_cost,
559 p->plan_rows, p->plan_width);
565 rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
566 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
568 else if (IsA(p, IndexScan))
572 rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
573 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
575 else if (IsA(p, FunctionScan))
579 rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
580 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
584 if (extraInfo[0] != '\0')
585 printf(" ( %s )\n", extraInfo);
588 print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
589 print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
595 Append *appendplan = (Append *) p;
597 foreach(l, appendplan->appendplans)
599 Plan *subnode = (Plan *) lfirst(l);
602 * I don't think we need to fiddle with the range table here,
605 print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
613 prints just the plan node types */
616 print_plan(Plan *p, Query *parsetree)
618 print_plan_recursive(p, parsetree, 0, "");