1 /*-------------------------------------------------------------------------
4 * various print routines (used mostly for debugging)
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.58 2002/12/12 15:49:28 tgl 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);
85 elog(lev, "%s:\n%s", title, f);
90 * Format a nodeToString output for display on a terminal.
92 * The result is a palloc'd string.
94 * This version just tries to break at whitespace.
97 format_node_dump(const char *dump)
100 char line[LINELEN + 1];
106 initStringInfo(&str);
110 for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++)
116 /* ok to break at adjacent space */
121 for (k = j - 1; k > 0; k--)
126 /* back up; will reprint all after space */
132 appendStringInfo(&str, "%s\n", line);
137 appendStringInfo(&str, "%s\n", line);
144 * Format a nodeToString output for display on a terminal.
146 * The result is a palloc'd string.
148 * This version tries to indent intelligently.
151 pretty_format_node_dump(const char *dump)
156 char line[LINELEN + 1];
163 initStringInfo(&str);
164 indentLev = 0; /* logical indent level */
165 indentDist = 0; /* physical indent distance */
169 for (j = 0; j < indentDist; j++)
171 for (; j < LINELEN && dump[i] != '\0'; i++, j++)
179 /* print data before the } */
181 appendStringInfo(&str, "%s\n", line);
183 /* print the } at indentDist */
184 line[indentDist] = '}';
185 line[indentDist + 1] = '\0';
186 appendStringInfo(&str, "%s\n", line);
191 indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
194 /* j will equal indentDist on next loop iteration */
197 /* force line break after ')' */
199 appendStringInfo(&str, "%s\n", line);
203 /* force line break before { */
207 appendStringInfo(&str, "%s\n", line);
211 indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
212 for (j = 0; j < indentDist; j++)
217 /* force line break before : */
221 appendStringInfo(&str, "%s\n", line);
231 appendStringInfo(&str, "%s\n", line);
234 appendStringInfo(&str, "%s\n", line);
243 * print contents of range table
246 print_rt(List *rtable)
251 printf("resno\trefname \trelid\tinFromCl\n");
252 printf("-----\t---------\t-----\t--------\n");
255 RangeTblEntry *rte = lfirst(l);
257 switch (rte->rtekind)
261 i, rte->eref->aliasname, rte->relid);
264 printf("%d\t%s\t[subquery]",
265 i, rte->eref->aliasname);
268 printf("%d\t%s\t[rangefunction]",
269 i, rte->eref->aliasname);
272 printf("%d\t%s\t[join]",
273 i, rte->eref->aliasname);
276 printf("%d\t%s\t[special]",
277 i, rte->eref->aliasname);
280 printf("%d\t%s\t[unknown rtekind]",
281 i, rte->eref->aliasname);
285 (rte->inh ? "inh" : ""),
286 (rte->inFromCl ? "inFromCl" : ""));
294 * print an expression
297 print_expr(Node *expr, List *rtable)
307 Var *var = (Var *) expr;
325 Assert(var->varno > 0 &&
326 (int) var->varno <= length(rtable));
327 rte = rt_fetch(var->varno, rtable);
328 relname = rte->eref->aliasname;
329 attname = get_rte_attribute_name(rte, var->varattno);
333 printf("%s.%s", relname, attname);
335 else if (IsA(expr, Const))
337 Const *c = (Const *) expr;
349 typeTup = SearchSysCache(TYPEOID,
350 ObjectIdGetDatum(c->consttype),
352 if (!HeapTupleIsValid(typeTup))
353 elog(ERROR, "Cache lookup for type %u failed", c->consttype);
354 typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
355 typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
356 ReleaseSysCache(typeTup);
358 outputstr = DatumGetCString(OidFunctionCall3(typoutput,
360 ObjectIdGetDatum(typelem),
362 printf("%s", outputstr);
365 else if (IsA(expr, Expr))
367 Expr *e = (Expr *) expr;
369 if (is_opclause(expr))
373 print_expr((Node *) get_leftop(e), rtable);
374 opname = get_opname(((OpExpr *) e)->opno);
375 printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
376 print_expr((Node *) get_rightop(e), rtable);
382 printf("not an expr");
387 * pathkeys list of list of PathKeyItems
390 print_pathkeys(List *pathkeys, List *rtable)
398 List *pathkey = lfirst(i);
403 PathKeyItem *item = lfirst(k);
405 print_expr(item->key, rtable);
418 * print targetlist in a more legible way.
421 print_tl(List *tlist, List *rtable)
428 TargetEntry *tle = lfirst(tl);
430 printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname);
431 if (tle->resdom->reskey != 0)
432 printf("(%d):\t", tle->resdom->reskey);
435 print_expr((Node *) tle->expr, rtable);
443 * print out the tuple with the given TupleTableSlot
446 print_slot(TupleTableSlot *slot)
450 printf("tuple is null.\n");
453 if (!slot->ttc_tupleDescriptor)
455 printf("no tuple descriptor.\n");
459 debugtup(slot->val, slot->ttc_tupleDescriptor, NULL);
463 plannode_type(Plan *p)
482 return "SUBQUERYSCAN";
484 return "FUNCTIONSCAN";
515 * Recursively prints a simple text description of the plan tree
518 print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
521 char extraInfo[NAMEDATALEN + 100];
525 for (i = 0; i < indentLevel; i++)
527 printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
528 p->startup_cost, p->total_cost,
529 p->plan_rows, p->plan_width);
535 rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
536 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
538 else if (IsA(p, IndexScan))
542 rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
543 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
545 else if (IsA(p, FunctionScan))
549 rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
550 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
554 if (extraInfo[0] != '\0')
555 printf(" ( %s )\n", extraInfo);
558 print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
559 print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
565 Append *appendplan = (Append *) p;
567 foreach(lst, appendplan->appendplans)
569 Plan *subnode = (Plan *) lfirst(lst);
572 * I don't think we need to fiddle with the range table here,
575 print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
583 prints just the plan node types */
586 print_plan(Plan *p, Query *parsetree)
588 print_plan_recursive(p, parsetree, 0, "");