55 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
66 * Portions Copyright (c) 1994-5, Regents of the University of California
77 *
8- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.70 2002/03/06 06:09:33 momjian Exp $
8+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.71 2002/03/12 00:51:35 tgl Exp $
99 *
1010 */
1111
1515#include "executor/instrument.h"
1616#include "lib/stringinfo.h"
1717#include "nodes/print.h"
18+ #include "optimizer/clauses.h"
1819#include "optimizer/planner.h"
1920#include "parser/parsetree.h"
2021#include "rewrite/rewriteHandler.h"
2122#include "tcop/pquery.h"
23+ #include "utils/builtins.h"
2224#include "utils/relcache.h"
2325
26+
2427typedef struct ExplainState
2528{
2629 /* options */
@@ -32,6 +35,14 @@ typedef struct ExplainState
3235
3336static StringInfo Explain_PlanToString (Plan * plan , ExplainState * es );
3437static void ExplainOneQuery (Query * query , bool verbose , bool analyze , CommandDest dest );
38+ static void show_scan_qual (List * qual , bool is_or_qual , const char * qlabel ,
39+ int scanrelid ,
40+ StringInfo str , int indent , ExplainState * es );
41+ static void show_upper_qual (List * qual , const char * qlabel ,
42+ const char * outer_name , int outer_varno , Plan * outer_plan ,
43+ const char * inner_name , int inner_varno , Plan * inner_plan ,
44+ StringInfo str , int indent , ExplainState * es );
45+ static Node * make_ors_ands_explicit (List * orclauses );
3546
3647/* Convert a null string pointer into "<>" */
3748#define stringStringInfo (s ) (((s) == NULL) ? "<>" : (s))
@@ -40,7 +51,6 @@ static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDes
4051/*
4152 * ExplainQuery -
4253 * print out the execution plan for a given query
43- *
4454 */
4555void
4656ExplainQuery (Query * query , bool verbose , bool analyze , CommandDest dest )
@@ -81,7 +91,6 @@ ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
8191/*
8292 * ExplainOneQuery -
8393 * print out the execution plan for one query
84- *
8594 */
8695static void
8796ExplainOneQuery (Query * query , bool verbose , bool analyze , CommandDest dest )
@@ -176,9 +185,6 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
176185 pfree (es );
177186}
178187
179- /*****************************************************************************
180- *
181- *****************************************************************************/
182188
183189/*
184190 * explain_outNode -
@@ -341,6 +347,90 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
341347 }
342348 appendStringInfo (str , "\n" );
343349
350+ /* quals */
351+ switch (nodeTag (plan ))
352+ {
353+ case T_IndexScan :
354+ show_scan_qual (((IndexScan * ) plan )-> indxqualorig , true,
355+ "indxqual" ,
356+ ((Scan * ) plan )-> scanrelid ,
357+ str , indent , es );
358+ show_scan_qual (plan -> qual , false, "qual" ,
359+ ((Scan * ) plan )-> scanrelid ,
360+ str , indent , es );
361+ break ;
362+ case T_SeqScan :
363+ case T_TidScan :
364+ show_scan_qual (plan -> qual , false, "qual" ,
365+ ((Scan * ) plan )-> scanrelid ,
366+ str , indent , es );
367+ break ;
368+ case T_NestLoop :
369+ show_upper_qual (((NestLoop * ) plan )-> join .joinqual , "joinqual" ,
370+ "outer" , OUTER , outerPlan (plan ),
371+ "inner" , INNER , innerPlan (plan ),
372+ str , indent , es );
373+ show_upper_qual (plan -> qual , "qual" ,
374+ "outer" , OUTER , outerPlan (plan ),
375+ "inner" , INNER , innerPlan (plan ),
376+ str , indent , es );
377+ break ;
378+ case T_MergeJoin :
379+ show_upper_qual (((MergeJoin * ) plan )-> mergeclauses , "merge" ,
380+ "outer" , OUTER , outerPlan (plan ),
381+ "inner" , INNER , innerPlan (plan ),
382+ str , indent , es );
383+ show_upper_qual (((MergeJoin * ) plan )-> join .joinqual , "joinqual" ,
384+ "outer" , OUTER , outerPlan (plan ),
385+ "inner" , INNER , innerPlan (plan ),
386+ str , indent , es );
387+ show_upper_qual (plan -> qual , "qual" ,
388+ "outer" , OUTER , outerPlan (plan ),
389+ "inner" , INNER , innerPlan (plan ),
390+ str , indent , es );
391+ break ;
392+ case T_HashJoin :
393+ show_upper_qual (((HashJoin * ) plan )-> hashclauses , "hash" ,
394+ "outer" , OUTER , outerPlan (plan ),
395+ "inner" , INNER , innerPlan (plan ),
396+ str , indent , es );
397+ show_upper_qual (((HashJoin * ) plan )-> join .joinqual , "joinqual" ,
398+ "outer" , OUTER , outerPlan (plan ),
399+ "inner" , INNER , innerPlan (plan ),
400+ str , indent , es );
401+ show_upper_qual (plan -> qual , "qual" ,
402+ "outer" , OUTER , outerPlan (plan ),
403+ "inner" , INNER , innerPlan (plan ),
404+ str , indent , es );
405+ break ;
406+ case T_SubqueryScan :
407+ show_upper_qual (plan -> qual , "qual" ,
408+ "subplan" , 1 , ((SubqueryScan * ) plan )-> subplan ,
409+ "" , 0 , NULL ,
410+ str , indent , es );
411+ break ;
412+ case T_Agg :
413+ case T_Group :
414+ show_upper_qual (plan -> qual , "qual" ,
415+ "subplan" , 0 , outerPlan (plan ),
416+ "" , 0 , NULL ,
417+ str , indent , es );
418+ break ;
419+ case T_Result :
420+ show_upper_qual ((List * ) ((Result * ) plan )-> resconstantqual ,
421+ "constqual" ,
422+ "subplan" , OUTER , outerPlan (plan ),
423+ "" , 0 , NULL ,
424+ str , indent , es );
425+ show_upper_qual (plan -> qual , "qual" ,
426+ "subplan" , OUTER , outerPlan (plan ),
427+ "" , 0 , NULL ,
428+ str , indent , es );
429+ break ;
430+ default :
431+ break ;
432+ }
433+
344434 /* initPlan-s */
345435 if (plan -> initPlan )
346436 {
@@ -448,3 +538,121 @@ Explain_PlanToString(Plan *plan, ExplainState *es)
448538 explain_outNode (str , plan , 0 , es );
449539 return str ;
450540}
541+
542+ /*
543+ * Show a qualifier expression for a scan plan node
544+ */
545+ static void
546+ show_scan_qual (List * qual , bool is_or_qual , const char * qlabel ,
547+ int scanrelid ,
548+ StringInfo str , int indent , ExplainState * es )
549+ {
550+ RangeTblEntry * rte ;
551+ List * context ;
552+ Node * node ;
553+ char * exprstr ;
554+ int i ;
555+
556+ /* No work if empty qual */
557+ if (qual == NIL )
558+ return ;
559+ if (is_or_qual )
560+ {
561+ if (lfirst (qual ) == NIL && lnext (qual ) == NIL )
562+ return ;
563+ }
564+
565+ /* Generate deparse context */
566+ Assert (scanrelid > 0 && scanrelid <= length (es -> rtable ));
567+ rte = rt_fetch (scanrelid , es -> rtable );
568+
569+ /* Assume it's on a real relation */
570+ Assert (rte -> relname );
571+
572+ context = deparse_context_for (rte -> relname , rte -> relid );
573+
574+ /* Fix qual --- indexqual requires different processing */
575+ if (is_or_qual )
576+ node = make_ors_ands_explicit (qual );
577+ else
578+ node = (Node * ) make_ands_explicit (qual );
579+
580+ /* Deparse the expression */
581+ exprstr = deparse_expression (node , context , false);
582+
583+ /* And add to str */
584+ for (i = 0 ; i < indent ; i ++ )
585+ appendStringInfo (str , " " );
586+ appendStringInfo (str , " %s: %s\n" , qlabel , exprstr );
587+ }
588+
589+ /*
590+ * Show a qualifier expression for an upper-level plan node
591+ */
592+ static void
593+ show_upper_qual (List * qual , const char * qlabel ,
594+ const char * outer_name , int outer_varno , Plan * outer_plan ,
595+ const char * inner_name , int inner_varno , Plan * inner_plan ,
596+ StringInfo str , int indent , ExplainState * es )
597+ {
598+ List * context ;
599+ Node * outercontext ;
600+ Node * innercontext ;
601+ Node * node ;
602+ char * exprstr ;
603+ int i ;
604+
605+ /* No work if empty qual */
606+ if (qual == NIL )
607+ return ;
608+
609+ /* Generate deparse context */
610+ if (outer_plan )
611+ outercontext = deparse_context_for_subplan (outer_name ,
612+ outer_plan -> targetlist ,
613+ es -> rtable );
614+ else
615+ outercontext = NULL ;
616+ if (inner_plan )
617+ innercontext = deparse_context_for_subplan (inner_name ,
618+ inner_plan -> targetlist ,
619+ es -> rtable );
620+ else
621+ innercontext = NULL ;
622+ context = deparse_context_for_plan (outer_varno , outercontext ,
623+ inner_varno , innercontext );
624+
625+ /* Deparse the expression */
626+ node = (Node * ) make_ands_explicit (qual );
627+ exprstr = deparse_expression (node , context , (inner_plan != NULL ));
628+
629+ /* And add to str */
630+ for (i = 0 ; i < indent ; i ++ )
631+ appendStringInfo (str , " " );
632+ appendStringInfo (str , " %s: %s\n" , qlabel , exprstr );
633+ }
634+
635+ /*
636+ * Indexscan qual lists have an implicit OR-of-ANDs structure. Make it
637+ * explicit so deparsing works properly.
638+ */
639+ static Node *
640+ make_ors_ands_explicit (List * orclauses )
641+ {
642+ if (orclauses == NIL )
643+ return NULL ; /* probably can't happen */
644+ else if (lnext (orclauses ) == NIL )
645+ return (Node * ) make_ands_explicit (lfirst (orclauses ));
646+ else
647+ {
648+ List * args = NIL ;
649+ List * orptr ;
650+
651+ foreach (orptr , orclauses )
652+ {
653+ args = lappend (args , make_ands_explicit (lfirst (orptr )));
654+ }
655+
656+ return (Node * ) make_orclause (args );
657+ }
658+ }
0 commit comments