32
32
#include "parser/analyze.h"
33
33
#include "parser/parsetree.h"
34
34
#include "rewrite/rewriteHandler.h"
35
+ #include "storage/aio_subsys.h"
35
36
#include "storage/bufmgr.h"
36
37
#include "tcop/tcopprot.h"
37
38
#include "utils/builtins.h"
@@ -144,6 +145,8 @@ static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
144
145
static const char * explain_get_index_name (Oid indexId );
145
146
static bool peek_buffer_usage (ExplainState * es , const BufferUsage * usage );
146
147
static void show_buffer_usage (ExplainState * es , const BufferUsage * usage );
148
+ static bool peek_storageio_usage (ExplainState * es , const StorageIOUsage * usage );
149
+ static void show_storageio_usage (ExplainState * es , const StorageIOUsage * usage );
147
150
static void show_wal_usage (ExplainState * es , const WalUsage * usage );
148
151
static void show_memory_counters (ExplainState * es ,
149
152
const MemoryContextCounters * mem_counters );
@@ -325,6 +328,8 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
325
328
planduration ;
326
329
BufferUsage bufusage_start ,
327
330
bufusage ;
331
+ StorageIOUsage storageio ,
332
+ storageio_start ;
328
333
MemoryContextCounters mem_counters ;
329
334
MemoryContext planner_ctx = NULL ;
330
335
MemoryContext saved_ctx = NULL ;
@@ -346,7 +351,10 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
346
351
}
347
352
348
353
if (es -> buffers )
354
+ {
349
355
bufusage_start = pgBufferUsage ;
356
+ GetStorageIOUsage (& storageio_start );
357
+ }
350
358
INSTR_TIME_SET_CURRENT (planstart );
351
359
352
360
/* plan the query */
@@ -361,16 +369,20 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
361
369
MemoryContextMemConsumed (planner_ctx , & mem_counters );
362
370
}
363
371
364
- /* calc differences of buffer counters. */
372
+ /* calc differences of buffer and storage I/O counters. */
365
373
if (es -> buffers )
366
374
{
367
375
memset (& bufusage , 0 , sizeof (BufferUsage ));
368
376
BufferUsageAccumDiff (& bufusage , & pgBufferUsage , & bufusage_start );
377
+
378
+ GetStorageIOUsage (& storageio );
379
+ StorageIOUsageDiff (& storageio , & storageio_start );
369
380
}
370
381
371
382
/* run it (if needed) and produce output */
372
383
ExplainOnePlan (plan , into , es , queryString , params , queryEnv ,
373
384
& planduration , (es -> buffers ? & bufusage : NULL ),
385
+ es -> buffers ? & storageio : NULL ,
374
386
es -> memory ? & mem_counters : NULL );
375
387
}
376
388
494
506
ExplainOnePlan (PlannedStmt * plannedstmt , IntoClause * into , ExplainState * es ,
495
507
const char * queryString , ParamListInfo params ,
496
508
QueryEnvironment * queryEnv , const instr_time * planduration ,
497
- const BufferUsage * bufusage ,
509
+ const BufferUsage * bufusage , const StorageIOUsage * planstorageio ,
498
510
const MemoryContextCounters * mem_counters )
499
511
{
500
512
DestReceiver * dest ;
@@ -504,6 +516,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
504
516
int eflags ;
505
517
int instrument_option = 0 ;
506
518
SerializeMetrics serializeMetrics = {0 };
519
+ StorageIOUsage storageio_start ;
507
520
508
521
Assert (plannedstmt -> commandType != CMD_UTILITY );
509
522
@@ -513,7 +526,19 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
513
526
instrument_option |= INSTRUMENT_ROWS ;
514
527
515
528
if (es -> buffers )
529
+ {
530
+ GetStorageIOUsage (& storageio_start );
531
+
532
+ /*
533
+ * Initialize global variable counters for parallel query workers.
534
+ * Even if the query is cancelled on the way, the EXPLAIN execution
535
+ * always passes here, so it can be initialized here.
536
+ */
537
+ pgStorageIOUsageParallel .inblock = 0 ;
538
+ pgStorageIOUsageParallel .outblock = 0 ;
539
+
516
540
instrument_option |= INSTRUMENT_BUFFERS ;
541
+ }
517
542
if (es -> wal )
518
543
instrument_option |= INSTRUMENT_WAL ;
519
544
@@ -597,8 +622,9 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
597
622
/* Create textual dump of plan tree */
598
623
ExplainPrintPlan (es , queryDesc );
599
624
600
- /* Show buffer and/or memory usage in planning */
601
- if (peek_buffer_usage (es , bufusage ) || mem_counters )
625
+ /* Show buffer, storage I/O, and/or memory usage in planning */
626
+ if (peek_buffer_usage (es , bufusage ) || peek_storageio_usage (es , planstorageio ) ||
627
+ mem_counters )
602
628
{
603
629
ExplainOpenGroup ("Planning" , "Planning" , true, es );
604
630
@@ -610,8 +636,10 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
610
636
}
611
637
612
638
if (bufusage )
639
+ {
613
640
show_buffer_usage (es , bufusage );
614
-
641
+ show_storageio_usage (es , planstorageio );
642
+ }
615
643
if (mem_counters )
616
644
show_memory_counters (es , mem_counters );
617
645
@@ -668,6 +696,34 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
668
696
669
697
totaltime += elapsed_time (& starttime );
670
698
699
+ /* Show storage I/O usage in execution */
700
+ if (es -> buffers )
701
+ {
702
+ StorageIOUsage storageio ;
703
+
704
+ GetStorageIOUsage (& storageio );
705
+ StorageIOUsageDiff (& storageio , & storageio_start );
706
+ StorageIOUsageAdd (& storageio , & pgStorageIOUsageParallel );
707
+
708
+ if (peek_storageio_usage (es , & storageio ))
709
+ {
710
+ ExplainOpenGroup ("Execution" , "Execution" , true, es );
711
+
712
+ if (es -> format == EXPLAIN_FORMAT_TEXT )
713
+ {
714
+ ExplainIndentText (es );
715
+ appendStringInfoString (es -> str , "Execution:\n" );
716
+ es -> indent ++ ;
717
+ }
718
+ show_storageio_usage (es , & storageio );
719
+
720
+ if (es -> format == EXPLAIN_FORMAT_TEXT )
721
+ es -> indent -- ;
722
+
723
+ ExplainCloseGroup ("Execution" , "Execution" , true, es );
724
+ }
725
+ }
726
+
671
727
/*
672
728
* We only report execution time if we actually ran the query (that is,
673
729
* the user specified ANALYZE), and if summary reporting is enabled (the
@@ -4252,6 +4308,65 @@ show_buffer_usage(ExplainState *es, const BufferUsage *usage)
4252
4308
}
4253
4309
}
4254
4310
4311
+ /*
4312
+ * Return whether show_storageio_usage would have anything to print, if given
4313
+ * the same 'usage' data. Note that when the format is anything other than
4314
+ * text, we print even if the counters are all zeroes.
4315
+ */
4316
+ static bool
4317
+ peek_storageio_usage (ExplainState * es , const StorageIOUsage * usage )
4318
+ {
4319
+ if (usage == NULL )
4320
+ return false;
4321
+
4322
+ /*
4323
+ * Since showing only the I/O excluding AIO workers underestimates the
4324
+ * total I/O, treat this case as having nothing to print.
4325
+ */
4326
+ if (pgaio_workers_enabled ())
4327
+ return false;
4328
+
4329
+ if (es -> format != EXPLAIN_FORMAT_TEXT )
4330
+ return true;
4331
+
4332
+ return usage -> inblock > 0 || usage -> outblock > 0 ;
4333
+ }
4334
+
4335
+ /*
4336
+ * Show storage I/O usage.
4337
+ */
4338
+ static void
4339
+ show_storageio_usage (ExplainState * es , const StorageIOUsage * usage )
4340
+ {
4341
+ /*
4342
+ * Since showing only the I/O excluding AIO workers underestimates the
4343
+ * total I/O, do not show anything.
4344
+ */
4345
+ if (pgaio_workers_enabled ())
4346
+ return ;
4347
+
4348
+ if (es -> format == EXPLAIN_FORMAT_TEXT )
4349
+ {
4350
+ /* Show only positive counter values. */
4351
+ if (usage -> inblock <= 0 && usage -> outblock <= 0 )
4352
+ return ;
4353
+
4354
+ ExplainIndentText (es );
4355
+ appendStringInfoString (es -> str , "Storage I/O:" );
4356
+ appendStringInfo (es -> str , " read=%ld times" , (long ) usage -> inblock );
4357
+ appendStringInfo (es -> str , " write=%ld times" , (long ) usage -> outblock );
4358
+
4359
+ appendStringInfoChar (es -> str , '\n' );
4360
+ }
4361
+ else
4362
+ {
4363
+ ExplainPropertyInteger ("Storage I/O Read" , NULL ,
4364
+ usage -> inblock , es );
4365
+ ExplainPropertyInteger ("Storage I/O Read" , NULL ,
4366
+ usage -> outblock , es );
4367
+ }
4368
+ }
4369
+
4255
4370
/*
4256
4371
* Show WAL usage details.
4257
4372
*/
0 commit comments