1 /*
2 * This file and its contents are licensed under the Timescale License.
3 * Please see the included NOTICE for copyright information and
4 * LICENSE-TIMESCALE for a copy of the license.
5 */
6
7 /*
8 * This file contains source code that was copied and/or modified from
9 * the PostgreSQL database, which is licensed under the open-source
10 * PostgreSQL License. Please see the NOTICE at the top level
11 * directory for a copy of the PostgreSQL License.
12 */
13
14 /*
15 * The code is partially copied from nodes/print.c and
16 * backend/optimizer/path/allpaths.c in the PostgreSQL source code, but we
17 * cannot use it out of the box for two reasons:
18 *
19 * The first reason is that the PostgreSQL code prints to standard output
20 * (hence to the log) and we want to build a string buffer to send back in a
21 * notice, we cannot use the functions as they are but have re-implement them.
22 *
23 * We want to send back paths and plans in a notice to the client, to make it
24 * possible to interactively investigate what paths and plans that queries
25 * generate without having to access the log.
26 *
27 * The second reason is that the PostgreSQL code is not aware of our custom
28 * nodes and the hierarchy below them, so we need to have special handling of
29 * custom nodes to get out more information.
30 *
31 * (A third reason is that the printing functions are incomplete and do not
32 * print items below certain nodes, such as Append and MergeAppend, and we are
33 * using them for our purposes and need to have more information about
34 * subpaths than what PostgreSQL prints.)
35 */
36
37 #include <postgres.h>
38 #include <foreign/fdwapi.h>
39
40 #include <access/printtup.h>
41 #include <nodes/nodeFuncs.h>
42 #include <nodes/pg_list.h>
43 #include <optimizer/clauses.h>
44 #include <parser/parsetree.h>
45 #include <utils/builtins.h>
46 #include <utils/lsyscache.h>
47 #include <utils/varlena.h>
48 #include <nodes/extensible.h>
49
50 #include <compat/compat.h>
51 #include "fdw/relinfo.h"
52 #include "fdw/fdw_utils.h"
53 #include "debug.h"
54
55 static void append_expr(StringInfo buf, const Node *expr, const List *rtable);
56 static void tsl_debug_append_pathlist(StringInfo buf, PlannerInfo *root, List *pathlist, int indent,
57 bool isconsidered);
58
59 static const char *reloptkind_name[] = {
60 [RELOPT_BASEREL] = "BASEREL",
61 [RELOPT_JOINREL] = "JOINREL",
62 [RELOPT_OTHER_MEMBER_REL] = "OTHER_MEMBER_REL",
63 [RELOPT_OTHER_JOINREL] = "OTHER_JOINREL",
64 [RELOPT_UPPER_REL] = "UPPER_REL",
65 [RELOPT_OTHER_UPPER_REL] = "OTHER_UPPER_REL",
66 [RELOPT_DEADREL] = "DEADREL",
67 };
68
69 /* clang-format off */
70 static const char *upperrel_stage_name[] = {
71 [UPPERREL_SETOP] = "SETOP",
72 [UPPERREL_PARTIAL_GROUP_AGG] = "PARTIAL_GROUP_AGG",
73 [UPPERREL_GROUP_AGG] = "GROUP_AGG",
74 [UPPERREL_WINDOW] = "WINDOW",
75 [UPPERREL_DISTINCT] = "DISTINCT",
76 [UPPERREL_ORDERED] = "ORDERED",
77 [UPPERREL_FINAL] = "FINAL",
78 };
79 /* clang-format on */
80
81 static const char *fdw_rel_type_names[] = {
82 [TS_FDW_RELINFO_HYPERTABLE_DATA_NODE] = "DATA_NODE",
83 [TS_FDW_RELINFO_HYPERTABLE] = "HYPERTABLE",
84 [TS_FDW_RELINFO_FOREIGN_TABLE] = "FOREIGN_TABLE",
85 };
86
87 static void
append_var_expr(StringInfo buf,const Node * expr,const List * rtable)88 append_var_expr(StringInfo buf, const Node *expr, const List *rtable)
89 {
90 const Var *var = (const Var *) expr;
91 char *relname, *attname;
92
93 switch (var->varno)
94 {
95 case INNER_VAR:
96 relname = "INNER";
97 attname = "?";
98 break;
99 case OUTER_VAR:
100 relname = "OUTER";
101 attname = "?";
102 break;
103 case INDEX_VAR:
104 relname = "INDEX";
105 attname = "?";
106 break;
107 default:
108 {
109 RangeTblEntry *rte;
110
111 Assert(var->varno > 0 && (int) var->varno <= list_length(rtable));
112 rte = rt_fetch(var->varno, rtable);
113 relname = rte->eref->aliasname;
114 attname = get_rte_attribute_name(rte, var->varattno);
115 }
116 break;
117 }
118 appendStringInfo(buf, "%s.%s", relname, attname);
119 }
120
121 static void
append_const_expr(StringInfo buf,const Node * expr,const List * rtable)122 append_const_expr(StringInfo buf, const Node *expr, const List *rtable)
123 {
124 const Const *c = (const Const *) expr;
125 Oid typoutput;
126 bool typIsVarlena;
127 char *outputstr;
128
129 if (c->constisnull)
130 {
131 appendStringInfo(buf, "NULL");
132 return;
133 }
134
135 getTypeOutputInfo(c->consttype, &typoutput, &typIsVarlena);
136
137 outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
138 appendStringInfo(buf, "%s", outputstr);
139 pfree(outputstr);
140 }
141
142 static void
append_op_expr(StringInfo buf,const Node * expr,const List * rtable)143 append_op_expr(StringInfo buf, const Node *expr, const List *rtable)
144 {
145 const OpExpr *e = (const OpExpr *) expr;
146 char *opname = get_opname(e->opno);
147 if (list_length(e->args) > 1)
148 {
149 append_expr(buf, get_leftop((const Expr *) e), rtable);
150 appendStringInfo(buf, " %s ", ((opname != NULL) ? opname : "(invalid operator)"));
151 append_expr(buf, get_rightop((const Expr *) e), rtable);
152 }
153 else
154 {
155 appendStringInfo(buf, "%s ", ((opname != NULL) ? opname : "(invalid operator)"));
156 append_expr(buf, get_leftop((const Expr *) e), rtable);
157 }
158 }
159
160 static void
append_func_expr(StringInfo buf,const Node * expr,const List * rtable)161 append_func_expr(StringInfo buf, const Node *expr, const List *rtable)
162 {
163 const FuncExpr *e = (const FuncExpr *) expr;
164 char *funcname = get_func_name(e->funcid);
165 ListCell *l;
166
167 appendStringInfo(buf, "%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
168 foreach (l, e->args)
169 {
170 append_expr(buf, lfirst(l), rtable);
171 if (lnext_compat(e->args, l))
172 appendStringInfoString(buf, ", ");
173 }
174 appendStringInfoChar(buf, ')');
175 }
176
177 static void
append_expr(StringInfo buf,const Node * expr,const List * rtable)178 append_expr(StringInfo buf, const Node *expr, const List *rtable)
179 {
180 if (expr == NULL)
181 {
182 appendStringInfo(buf, "<>");
183 return;
184 }
185
186 switch (nodeTag(expr))
187 {
188 case T_Var:
189 append_var_expr(buf, expr, rtable);
190 break;
191
192 case T_Const:
193 append_const_expr(buf, expr, rtable);
194 break;
195
196 case T_OpExpr:
197 append_op_expr(buf, expr, rtable);
198 break;
199
200 case T_FuncExpr:
201 append_func_expr(buf, expr, rtable);
202 break;
203
204 default:
205 appendStringInfo(buf, "unknown expr");
206 break;
207 }
208 }
209
210 static void
append_restrict_clauses(StringInfo buf,PlannerInfo * root,List * clauses)211 append_restrict_clauses(StringInfo buf, PlannerInfo *root, List *clauses)
212 {
213 ListCell *cell;
214
215 foreach (cell, clauses)
216 {
217 RestrictInfo *c = lfirst(cell);
218
219 append_expr(buf, (Node *) c->clause, root->parse->rtable);
220 if (lnext_compat(clauses, cell))
221 appendStringInfoString(buf, ", ");
222 }
223 }
224
225 static void
append_relids(StringInfo buf,PlannerInfo * root,Relids relids)226 append_relids(StringInfo buf, PlannerInfo *root, Relids relids)
227 {
228 int x = -1;
229 bool first = true;
230
231 while ((x = bms_next_member(relids, x)) >= 0)
232 {
233 if (!first)
234 appendStringInfoChar(buf, ' ');
235 if (x < root->simple_rel_array_size && root->simple_rte_array[x])
236 appendStringInfo(buf, "%s", root->simple_rte_array[x]->eref->aliasname);
237 else
238 appendStringInfo(buf, "%d", x);
239 first = false;
240 }
241 }
242
243 static void
append_pathkeys(StringInfo buf,const List * pathkeys,const List * rtable)244 append_pathkeys(StringInfo buf, const List *pathkeys, const List *rtable)
245 {
246 const ListCell *i;
247
248 appendStringInfoChar(buf, '(');
249 foreach (i, pathkeys)
250 {
251 PathKey *pathkey = (PathKey *) lfirst(i);
252 EquivalenceClass *eclass;
253 ListCell *k;
254 bool first = true;
255
256 eclass = pathkey->pk_eclass;
257 /* chase up, in case pathkey is non-canonical */
258 while (eclass->ec_merged)
259 eclass = eclass->ec_merged;
260
261 appendStringInfoChar(buf, '(');
262 foreach (k, eclass->ec_members)
263 {
264 EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
265
266 if (first)
267 first = false;
268 else
269 appendStringInfoString(buf, ", ");
270 append_expr(buf, (Node *) mem->em_expr, rtable);
271 }
272 appendStringInfoChar(buf, ')');
273 if (lnext_compat(pathkeys, i))
274 appendStringInfoString(buf, ", ");
275 }
276 appendStringInfoChar(buf, ')');
277 }
278
279 /*
280 * Return a relation's name.
281 *
282 * This function guarantees the return of a valid name string for a
283 * relation. For relations that have no unique name we return "-".
284 */
285 static const char *
get_relation_name(PlannerInfo * root,RelOptInfo * rel)286 get_relation_name(PlannerInfo *root, RelOptInfo *rel)
287 {
288 TsFdwRelInfo *fdw_info = fdw_relinfo_get(rel);
289
290 if (NULL != fdw_info)
291 return fdw_info->relation_name->data;
292
293 if (rel->reloptkind == RELOPT_BASEREL)
294 {
295 RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
296
297 return get_rel_name(rte->relid);
298 }
299
300 return "-";
301 }
302
303 /*
304 * Return a string name for the FDW type of a relation.
305 *
306 * For relations that are not an FDW relation we simply return "-".
307 */
308 static const char *
get_fdw_relation_typename(RelOptInfo * rel)309 get_fdw_relation_typename(RelOptInfo *rel)
310 {
311 TsFdwRelInfo *fdw_info = fdw_relinfo_get(rel);
312
313 if (NULL != fdw_info)
314 return fdw_rel_type_names[fdw_info->type];
315
316 return "-";
317 }
318
319 static void
tsl_debug_append_path(StringInfo buf,PlannerInfo * root,Path * path,int indent)320 tsl_debug_append_path(StringInfo buf, PlannerInfo *root, Path *path, int indent)
321 {
322 const char *ptype;
323 const char *extra_info = NULL;
324 bool join = false;
325 Path *subpath = NULL;
326 List *subpath_list = NULL;
327 int i;
328
329 switch (nodeTag(path))
330 {
331 case T_Path:
332 switch (path->pathtype)
333 {
334 case T_SeqScan:
335 ptype = "SeqScan";
336 break;
337 case T_SampleScan:
338 ptype = "SampleScan";
339 break;
340 case T_SubqueryScan:
341 ptype = "SubqueryScan";
342 break;
343 case T_FunctionScan:
344 ptype = "FunctionScan";
345 break;
346 case T_TableFuncScan:
347 ptype = "TableFuncScan";
348 break;
349 case T_ValuesScan:
350 ptype = "ValuesScan";
351 break;
352 case T_CteScan:
353 ptype = "CteScan";
354 break;
355 case T_WorkTableScan:
356 ptype = "WorkTableScan";
357 break;
358 default:
359 ptype = "???Path";
360 break;
361 }
362 break;
363 case T_IndexPath:
364 ptype = "IdxScan";
365 break;
366 case T_BitmapHeapPath:
367 ptype = "BitmapHeapScan";
368 break;
369 case T_BitmapAndPath:
370 ptype = "BitmapAndPath";
371 break;
372 case T_BitmapOrPath:
373 ptype = "BitmapOrPath";
374 break;
375 case T_TidPath:
376 ptype = "TidScan";
377 break;
378 case T_SubqueryScanPath:
379 ptype = "SubqueryScanScan";
380 subpath = castNode(SubqueryScanPath, path)->subpath;
381 break;
382 case T_ForeignPath:
383 ptype = "ForeignScan";
384 break;
385 case T_CustomPath:
386 ptype = "CustomScan";
387 subpath_list = castNode(CustomPath, path)->custom_paths;
388 extra_info = castNode(CustomPath, path)->methods->CustomName;
389 break;
390 case T_NestPath:
391 ptype = "NestLoop";
392 join = true;
393 break;
394 case T_MergePath:
395 ptype = "MergeJoin";
396 join = true;
397 break;
398 case T_HashPath:
399 ptype = "HashJoin";
400 join = true;
401 break;
402 case T_AppendPath:
403 ptype = "Append";
404 subpath_list = castNode(AppendPath, path)->subpaths;
405 break;
406 case T_MergeAppendPath:
407 ptype = "MergeAppend";
408 subpath_list = castNode(MergeAppendPath, path)->subpaths;
409 break;
410 case T_GroupResultPath:
411 ptype = "GroupResult";
412 break;
413 case T_MaterialPath:
414 ptype = "Material";
415 subpath = castNode(MaterialPath, path)->subpath;
416 break;
417 case T_UniquePath:
418 ptype = "Unique";
419 subpath = castNode(UniquePath, path)->subpath;
420 break;
421 case T_GatherPath:
422 ptype = "Gather";
423 subpath = castNode(GatherPath, path)->subpath;
424 break;
425 case T_GatherMergePath:
426 ptype = "GatherMerge";
427 subpath = castNode(GatherMergePath, path)->subpath;
428 break;
429 case T_ProjectionPath:
430 ptype = "Projection";
431 subpath = castNode(ProjectionPath, path)->subpath;
432 break;
433 case T_ProjectSetPath:
434 ptype = "ProjectSet";
435 subpath = castNode(ProjectSetPath, path)->subpath;
436 break;
437 case T_SortPath:
438 ptype = "Sort";
439 subpath = castNode(SortPath, path)->subpath;
440 break;
441 case T_GroupPath:
442 ptype = "Group";
443 subpath = castNode(GroupPath, path)->subpath;
444 break;
445 case T_UpperUniquePath:
446 ptype = "UpperUnique";
447 subpath = castNode(UpperUniquePath, path)->subpath;
448 break;
449 case T_AggPath:
450 ptype = "Agg";
451 subpath = castNode(AggPath, path)->subpath;
452 break;
453 case T_GroupingSetsPath:
454 ptype = "GroupingSets";
455 subpath = castNode(GroupingSetsPath, path)->subpath;
456 break;
457 case T_MinMaxAggPath:
458 ptype = "MinMaxAgg";
459 break;
460 case T_WindowAggPath:
461 ptype = "WindowAgg";
462 subpath = castNode(WindowAggPath, path)->subpath;
463 break;
464 case T_SetOpPath:
465 ptype = "SetOp";
466 subpath = castNode(SetOpPath, path)->subpath;
467 break;
468 case T_RecursiveUnionPath:
469 ptype = "RecursiveUnion";
470 break;
471 case T_LockRowsPath:
472 ptype = "LockRows";
473 subpath = castNode(LockRowsPath, path)->subpath;
474 break;
475 case T_ModifyTablePath:
476 ptype = "ModifyTable";
477 #if PG14_LT
478 subpath_list = castNode(ModifyTablePath, path)->subpaths;
479 #else
480 subpath_list = list_make1(castNode(ModifyTablePath, path)->subpath);
481 #endif
482 break;
483 case T_LimitPath:
484 ptype = "Limit";
485 subpath = castNode(LimitPath, path)->subpath;
486 break;
487 default:
488 ptype = "???Path";
489 break;
490 }
491
492 for (i = 0; i < indent; i++)
493 appendStringInfo(buf, "\t");
494 appendStringInfo(buf, "%s", ptype);
495 if (extra_info)
496 appendStringInfo(buf, " (%s)", extra_info);
497
498 if (path->parent)
499 {
500 appendStringInfo(buf,
501 " [rel type: %s, kind: %s",
502 get_fdw_relation_typename(path->parent),
503 reloptkind_name[path->parent->reloptkind]);
504 appendStringInfoString(buf, ", parent's base rels: ");
505 append_relids(buf, root, path->parent->relids);
506 appendStringInfoChar(buf, ']');
507 }
508
509 if (path->param_info)
510 {
511 appendStringInfoString(buf, " required_outer (");
512 append_relids(buf, root, path->param_info->ppi_req_outer);
513 appendStringInfoChar(buf, ')');
514 }
515
516 appendStringInfo(buf, " rows=%.0f", path->rows);
517
518 if (path->pathkeys)
519 {
520 appendStringInfoString(buf, " with pathkeys: ");
521 append_pathkeys(buf, path->pathkeys, root->parse->rtable);
522 }
523
524 appendStringInfoString(buf, "\n");
525
526 if (join)
527 {
528 JoinPath *jp = (JoinPath *) path;
529
530 for (i = 0; i < indent; i++)
531 appendStringInfoString(buf, "\t");
532 appendStringInfoString(buf, " clauses: ");
533 append_restrict_clauses(buf, root, jp->joinrestrictinfo);
534 appendStringInfoString(buf, "\n");
535
536 if (IsA(path, MergePath))
537 {
538 MergePath *mp = castNode(MergePath, path);
539
540 for (i = 0; i < indent; i++)
541 appendStringInfo(buf, "\t");
542 appendStringInfo(buf,
543 " sortouter=%d sortinner=%d materializeinner=%d\n",
544 ((mp->outersortkeys) ? 1 : 0),
545 ((mp->innersortkeys) ? 1 : 0),
546 ((mp->materialize_inner) ? 1 : 0));
547 }
548
549 tsl_debug_append_path(buf, root, jp->outerjoinpath, indent + 1);
550 tsl_debug_append_path(buf, root, jp->innerjoinpath, indent + 1);
551 }
552
553 if (subpath)
554 tsl_debug_append_path(buf, root, subpath, indent + 1);
555 if (subpath_list)
556 tsl_debug_append_pathlist(buf, root, subpath_list, indent + 1, false);
557 }
558
559 static void
tsl_debug_append_pathlist(StringInfo buf,PlannerInfo * root,List * pathlist,int indent,bool isconsidered)560 tsl_debug_append_pathlist(StringInfo buf, PlannerInfo *root, List *pathlist, int indent,
561 bool isconsidered)
562 {
563 ListCell *cell;
564 foreach (cell, pathlist)
565 {
566 Path *path = isconsidered ? ((ConsideredPath *) lfirst(cell))->path : lfirst(cell);
567 tsl_debug_append_path(buf, root, path, indent);
568 }
569 }
570
571 /*
572 * Check whether a path is the origin of a considered path.
573 *
574 * It is not possible to do a simple memcmp() of paths here because a path
575 * could be a (semi-)shallow copy. Therefore we use the origin of the
576 * ConsideredPath object.
577 */
578 static bool
path_is_origin(const Path * p1,const ConsideredPath * p2)579 path_is_origin(const Path *p1, const ConsideredPath *p2)
580 {
581 return p2->origin == (uintptr_t) p1;
582 }
583
584 /*
585 * Print paths that were pruned during planning.
586 *
587 * The pruned paths are those that have been considered but are not in the
588 * rel's pathlist.
589 */
590 static void
tsl_debug_append_pruned_pathlist(StringInfo buf,PlannerInfo * root,RelOptInfo * rel,int indent)591 tsl_debug_append_pruned_pathlist(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, int indent)
592 {
593 TsFdwRelInfo *fdw_info = fdw_relinfo_get(rel);
594 ListCell *lc1;
595
596 if (NULL == fdw_info || fdw_info->considered_paths == NIL)
597 return;
598
599 foreach (lc1, rel->pathlist)
600 {
601 Path *p1 = (Path *) lfirst(lc1);
602 ListCell *lc2;
603 #if PG13_LT
604 ListCell *prev = NULL;
605 #endif
606
607 foreach (lc2, fdw_info->considered_paths)
608 {
609 ConsideredPath *p2 = (ConsideredPath *) lfirst(lc2);
610
611 if (path_is_origin(p1, p2))
612 {
613 fdw_info->considered_paths =
614 list_delete_cell_compat(fdw_info->considered_paths, lc2, prev);
615 fdw_utils_free_path(p2);
616 break;
617 }
618 #if PG13_LT
619 prev = lc2;
620 #endif
621 }
622 }
623
624 if (fdw_info->considered_paths == NIL)
625 return;
626
627 appendStringInfoString(buf, "Pruned paths:\n");
628 tsl_debug_append_pathlist(buf, root, fdw_info->considered_paths, indent, true);
629
630 foreach (lc1, fdw_info->considered_paths)
631 fdw_utils_free_path(lfirst(lc1));
632
633 fdw_info->considered_paths = NIL;
634 }
635
636 void
tsl_debug_log_rel_with_paths(PlannerInfo * root,RelOptInfo * rel,UpperRelationKind * upper_stage)637 tsl_debug_log_rel_with_paths(PlannerInfo *root, RelOptInfo *rel, UpperRelationKind *upper_stage)
638 {
639 StringInfo buf = makeStringInfo();
640
641 if (upper_stage != NULL)
642 appendStringInfo(buf, "Upper rel stage %s:\n", upperrel_stage_name[*upper_stage]);
643
644 appendStringInfo(buf,
645 "RELOPTINFO [rel name: %s, type: %s, kind: %s, base rel names: ",
646 get_relation_name(root, rel),
647 get_fdw_relation_typename(rel),
648 reloptkind_name[rel->reloptkind]);
649 append_relids(buf, root, rel->relids);
650 appendStringInfoChar(buf, ']');
651 appendStringInfo(buf, " rows=%.0f width=%d\n", rel->rows, rel->reltarget->width);
652
653 appendStringInfoString(buf, "Path list:\n");
654 tsl_debug_append_pathlist(buf, root, rel->pathlist, 1, false);
655 tsl_debug_append_pruned_pathlist(buf, root, rel, 1);
656
657 if (rel->cheapest_parameterized_paths)
658 {
659 appendStringInfoString(buf, "\nCheapest parameterized paths:\n");
660 tsl_debug_append_pathlist(buf, root, rel->cheapest_parameterized_paths, 1, false);
661 }
662
663 if (rel->cheapest_startup_path)
664 {
665 appendStringInfoString(buf, "\nCheapest startup path:\n");
666 tsl_debug_append_path(buf, root, rel->cheapest_startup_path, 1);
667 }
668
669 if (rel->cheapest_total_path)
670 {
671 appendStringInfoString(buf, "\nCheapest total path:\n");
672 tsl_debug_append_path(buf, root, rel->cheapest_total_path, 1);
673 }
674
675 appendStringInfoString(buf, "\n");
676 ereport(DEBUG2, (errmsg_internal("%s", buf->data)));
677 }
678