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