@@ -119,9 +119,11 @@ static void show_instrumentation_count(const char *qlabel, int which,
119
119
static void show_foreignscan_info (ForeignScanState * fsstate , ExplainState * es );
120
120
static void show_eval_params (Bitmapset * bms_params , ExplainState * es );
121
121
static const char * explain_get_index_name (Oid indexId );
122
- static void show_buffer_usage (ExplainState * es , const BufferUsage * usage ,
123
- bool planning );
122
+ static bool peek_buffer_usage (ExplainState * es , const BufferUsage * usage );
123
+ static void show_buffer_usage ( ExplainState * es , const BufferUsage * usage );
124
124
static void show_wal_usage (ExplainState * es , const WalUsage * usage );
125
+ static void show_memory_counters (ExplainState * es ,
126
+ const MemoryContextCounters * mem_counters );
125
127
static void ExplainIndexScanDetails (Oid indexid , ScanDirection indexorderdir ,
126
128
ExplainState * es );
127
129
static void ExplainScanTarget (Scan * plan , ExplainState * es );
@@ -202,6 +204,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
202
204
summary_set = true;
203
205
es -> summary = defGetBoolean (opt );
204
206
}
207
+ else if (strcmp (opt -> defname , "memory" ) == 0 )
208
+ es -> memory = defGetBoolean (opt );
205
209
else if (strcmp (opt -> defname , "format" ) == 0 )
206
210
{
207
211
char * p = defGetString (opt );
@@ -397,6 +401,25 @@ ExplainOneQuery(Query *query, int cursorOptions,
397
401
planduration ;
398
402
BufferUsage bufusage_start ,
399
403
bufusage ;
404
+ MemoryContextCounters mem_counters ;
405
+ MemoryContext planner_ctx = NULL ;
406
+ MemoryContext saved_ctx = NULL ;
407
+
408
+ if (es -> memory )
409
+ {
410
+ /*
411
+ * Create a new memory context to measure planner's memory
412
+ * consumption accurately. Note that if the planner were to be
413
+ * modified to use a different memory context type, here we would
414
+ * be changing that to AllocSet, which might be undesirable.
415
+ * However, we don't have a way to create a context of the same
416
+ * type as another, so we pray and hope that this is OK.
417
+ */
418
+ planner_ctx = AllocSetContextCreate (CurrentMemoryContext ,
419
+ "explain analyze planner context" ,
420
+ ALLOCSET_DEFAULT_SIZES );
421
+ saved_ctx = MemoryContextSwitchTo (planner_ctx );
422
+ }
400
423
401
424
if (es -> buffers )
402
425
bufusage_start = pgBufferUsage ;
@@ -408,6 +431,12 @@ ExplainOneQuery(Query *query, int cursorOptions,
408
431
INSTR_TIME_SET_CURRENT (planduration );
409
432
INSTR_TIME_SUBTRACT (planduration , planstart );
410
433
434
+ if (es -> memory )
435
+ {
436
+ MemoryContextSwitchTo (saved_ctx );
437
+ MemoryContextMemConsumed (planner_ctx , & mem_counters );
438
+ }
439
+
411
440
/* calc differences of buffer counters. */
412
441
if (es -> buffers )
413
442
{
@@ -417,7 +446,8 @@ ExplainOneQuery(Query *query, int cursorOptions,
417
446
418
447
/* run it (if needed) and produce output */
419
448
ExplainOnePlan (plan , into , es , queryString , params , queryEnv ,
420
- & planduration , (es -> buffers ? & bufusage : NULL ));
449
+ & planduration , (es -> buffers ? & bufusage : NULL ),
450
+ es -> memory ? & mem_counters : NULL );
421
451
}
422
452
}
423
453
527
557
ExplainOnePlan (PlannedStmt * plannedstmt , IntoClause * into , ExplainState * es ,
528
558
const char * queryString , ParamListInfo params ,
529
559
QueryEnvironment * queryEnv , const instr_time * planduration ,
530
- const BufferUsage * bufusage )
560
+ const BufferUsage * bufusage ,
561
+ const MemoryContextCounters * mem_counters )
531
562
{
532
563
DestReceiver * dest ;
533
564
QueryDesc * queryDesc ;
@@ -615,11 +646,27 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
615
646
/* Create textual dump of plan tree */
616
647
ExplainPrintPlan (es , queryDesc );
617
648
618
- /* Show buffer usage in planning */
619
- if (bufusage )
649
+ /* Show buffer and/or memory usage in planning */
650
+ if (peek_buffer_usage ( es , bufusage ) || mem_counters )
620
651
{
621
652
ExplainOpenGroup ("Planning" , "Planning" , true, es );
622
- show_buffer_usage (es , bufusage , true);
653
+
654
+ if (es -> format == EXPLAIN_FORMAT_TEXT )
655
+ {
656
+ ExplainIndentText (es );
657
+ appendStringInfoString (es -> str , "Planning:\n" );
658
+ es -> indent ++ ;
659
+ }
660
+
661
+ if (bufusage )
662
+ show_buffer_usage (es , bufusage );
663
+
664
+ if (mem_counters )
665
+ show_memory_counters (es , mem_counters );
666
+
667
+ if (es -> format == EXPLAIN_FORMAT_TEXT )
668
+ es -> indent -- ;
669
+
623
670
ExplainCloseGroup ("Planning" , "Planning" , true, es );
624
671
}
625
672
@@ -2106,7 +2153,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
2106
2153
2107
2154
/* Show buffer/WAL usage */
2108
2155
if (es -> buffers && planstate -> instrument )
2109
- show_buffer_usage (es , & planstate -> instrument -> bufusage , false );
2156
+ show_buffer_usage (es , & planstate -> instrument -> bufusage );
2110
2157
if (es -> wal && planstate -> instrument )
2111
2158
show_wal_usage (es , & planstate -> instrument -> walusage );
2112
2159
@@ -2125,7 +2172,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
2125
2172
2126
2173
ExplainOpenWorker (n , es );
2127
2174
if (es -> buffers )
2128
- show_buffer_usage (es , & instrument -> bufusage , false );
2175
+ show_buffer_usage (es , & instrument -> bufusage );
2129
2176
if (es -> wal )
2130
2177
show_wal_usage (es , & instrument -> walusage );
2131
2178
ExplainCloseWorker (n , es );
@@ -3545,10 +3592,52 @@ explain_get_index_name(Oid indexId)
3545
3592
}
3546
3593
3547
3594
/*
3548
- * Show buffer usage details.
3595
+ * Return whether show_buffer_usage would have anything to print, if given
3596
+ * the same 'usage' data. Note that when the format is anything other than
3597
+ * text, we print even if the counters are all zeroes.
3598
+ */
3599
+ static bool
3600
+ peek_buffer_usage (ExplainState * es , const BufferUsage * usage )
3601
+ {
3602
+ bool has_shared ;
3603
+ bool has_local ;
3604
+ bool has_temp ;
3605
+ bool has_shared_timing ;
3606
+ bool has_local_timing ;
3607
+ bool has_temp_timing ;
3608
+
3609
+ if (usage == NULL )
3610
+ return false;
3611
+
3612
+ if (es -> format != EXPLAIN_FORMAT_TEXT )
3613
+ return true;
3614
+
3615
+ has_shared = (usage -> shared_blks_hit > 0 ||
3616
+ usage -> shared_blks_read > 0 ||
3617
+ usage -> shared_blks_dirtied > 0 ||
3618
+ usage -> shared_blks_written > 0 );
3619
+ has_local = (usage -> local_blks_hit > 0 ||
3620
+ usage -> local_blks_read > 0 ||
3621
+ usage -> local_blks_dirtied > 0 ||
3622
+ usage -> local_blks_written > 0 );
3623
+ has_temp = (usage -> temp_blks_read > 0 ||
3624
+ usage -> temp_blks_written > 0 );
3625
+ has_shared_timing = (!INSTR_TIME_IS_ZERO (usage -> shared_blk_read_time ) ||
3626
+ !INSTR_TIME_IS_ZERO (usage -> shared_blk_write_time ));
3627
+ has_local_timing = (!INSTR_TIME_IS_ZERO (usage -> local_blk_read_time ) ||
3628
+ !INSTR_TIME_IS_ZERO (usage -> local_blk_write_time ));
3629
+ has_temp_timing = (!INSTR_TIME_IS_ZERO (usage -> temp_blk_read_time ) ||
3630
+ !INSTR_TIME_IS_ZERO (usage -> temp_blk_write_time ));
3631
+
3632
+ return has_shared || has_local || has_temp || has_shared_timing ||
3633
+ has_local_timing || has_temp_timing ;
3634
+ }
3635
+
3636
+ /*
3637
+ * Show buffer usage details. This better be sync with peek_buffer_usage.
3549
3638
*/
3550
3639
static void
3551
- show_buffer_usage (ExplainState * es , const BufferUsage * usage , bool planning )
3640
+ show_buffer_usage (ExplainState * es , const BufferUsage * usage )
3552
3641
{
3553
3642
if (es -> format == EXPLAIN_FORMAT_TEXT )
3554
3643
{
@@ -3568,18 +3657,6 @@ show_buffer_usage(ExplainState *es, const BufferUsage *usage, bool planning)
3568
3657
!INSTR_TIME_IS_ZERO (usage -> local_blk_write_time ));
3569
3658
bool has_temp_timing = (!INSTR_TIME_IS_ZERO (usage -> temp_blk_read_time ) ||
3570
3659
!INSTR_TIME_IS_ZERO (usage -> temp_blk_write_time ));
3571
- bool show_planning = (planning && (has_shared ||
3572
- has_local || has_temp ||
3573
- has_shared_timing ||
3574
- has_local_timing ||
3575
- has_temp_timing ));
3576
-
3577
- if (show_planning )
3578
- {
3579
- ExplainIndentText (es );
3580
- appendStringInfoString (es -> str , "Planning:\n" );
3581
- es -> indent ++ ;
3582
- }
3583
3660
3584
3661
/* Show only positive counter values. */
3585
3662
if (has_shared || has_local || has_temp )
@@ -3678,9 +3755,6 @@ show_buffer_usage(ExplainState *es, const BufferUsage *usage, bool planning)
3678
3755
}
3679
3756
appendStringInfoChar (es -> str , '\n' );
3680
3757
}
3681
-
3682
- if (show_planning )
3683
- es -> indent -- ;
3684
3758
}
3685
3759
else
3686
3760
{
@@ -3766,6 +3840,32 @@ show_wal_usage(ExplainState *es, const WalUsage *usage)
3766
3840
}
3767
3841
}
3768
3842
3843
+ /*
3844
+ * Show memory usage details.
3845
+ */
3846
+ static void
3847
+ show_memory_counters (ExplainState * es , const MemoryContextCounters * mem_counters )
3848
+ {
3849
+ if (es -> format == EXPLAIN_FORMAT_TEXT )
3850
+ {
3851
+ ExplainIndentText (es );
3852
+ appendStringInfo (es -> str ,
3853
+ "Memory: used=%lld bytes allocated=%lld bytes" ,
3854
+ (long long ) (mem_counters -> totalspace - mem_counters -> freespace ),
3855
+ (long long ) mem_counters -> totalspace );
3856
+ appendStringInfoChar (es -> str , '\n' );
3857
+ }
3858
+ else
3859
+ {
3860
+ ExplainPropertyInteger ("Memory Used" , "bytes" ,
3861
+ mem_counters -> totalspace - mem_counters -> freespace ,
3862
+ es );
3863
+ ExplainPropertyInteger ("Memory Allocated" , "bytes" ,
3864
+ mem_counters -> totalspace , es );
3865
+ }
3866
+ }
3867
+
3868
+
3769
3869
/*
3770
3870
* Add some additional details about an IndexScan or IndexOnlyScan
3771
3871
*/
0 commit comments