aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/explain.c37
-rw-r--r--src/backend/utils/sort/tuplestore.c53
-rw-r--r--src/include/utils/tuplestore.h4
-rw-r--r--src/test/regress/expected/partition_prune.out37
-rw-r--r--src/test/regress/sql/partition_prune.sql29
5 files changed, 148 insertions, 12 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 30de9de9d4f..1b5ab503898 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -125,6 +125,7 @@ static void show_sort_info(SortState *sortstate, ExplainState *es);
static void show_incremental_sort_info(IncrementalSortState *incrsortstate,
ExplainState *es);
static void show_hash_info(HashState *hashstate, ExplainState *es);
+static void show_material_info(MaterialState *mstate, ExplainState *es);
static void show_memoize_info(MemoizeState *mstate, List *ancestors,
ExplainState *es);
static void show_hashagg_info(AggState *aggstate, ExplainState *es);
@@ -2251,6 +2252,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_Hash:
show_hash_info(castNode(HashState, planstate), es);
break;
+ case T_Material:
+ show_material_info(castNode(MaterialState, planstate), es);
+ break;
case T_Memoize:
show_memoize_info(castNode(MemoizeState, planstate), ancestors,
es);
@@ -3323,6 +3327,39 @@ show_hash_info(HashState *hashstate, ExplainState *es)
}
/*
+ * Show information on material node, storage method and maximum memory/disk
+ * space used.
+ */
+static void
+show_material_info(MaterialState *mstate, ExplainState *es)
+{
+ Tuplestorestate *tupstore;
+ const char *storageType;
+ int64 spaceUsedKB;
+
+ if (!es->analyze)
+ return;
+
+ tupstore = mstate->tuplestorestate;
+ storageType = tuplestore_storage_type_name(tupstore);
+ spaceUsedKB = BYTES_TO_KILOBYTES(tuplestore_space_used(tupstore));
+
+ if (es->format != EXPLAIN_FORMAT_TEXT)
+ {
+ ExplainPropertyText("Storage", storageType, es);
+ ExplainPropertyInteger("Maximum Storage", "kB", spaceUsedKB, es);
+ }
+ else
+ {
+ ExplainIndentText(es);
+ appendStringInfo(es->str,
+ "Storage: %s Maximum Storage: " INT64_FORMAT "kB\n",
+ storageType,
+ spaceUsedKB);
+ }
+}
+
+/*
* Show information on memoize hits/misses/evictions and memory usage.
*/
static void
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 947a868e569..24bb49ca874 100644
--- a/src/backend/utils/sort/tuplestore.c
+++ b/src/backend/utils/sort/tuplestore.c
@@ -109,6 +109,7 @@ struct Tuplestorestate
bool truncated; /* tuplestore_trim has removed tuples? */
int64 availMem; /* remaining memory available, in bytes */
int64 allowedMem; /* total memory allowed, in bytes */
+ int64 maxSpace; /* maximum space used in memory */
int64 tuples; /* number of tuples added */
BufFile *myfile; /* underlying file, or NULL if none */
MemoryContext context; /* memory context for holding tuples */
@@ -238,6 +239,7 @@ static Tuplestorestate *tuplestore_begin_common(int eflags,
int maxKBytes);
static void tuplestore_puttuple_common(Tuplestorestate *state, void *tuple);
static void dumptuples(Tuplestorestate *state);
+static void tuplestore_updatemax(Tuplestorestate *state);
static unsigned int getlen(Tuplestorestate *state, bool eofOK);
static void *copytup_heap(Tuplestorestate *state, void *tup);
static void writetup_heap(Tuplestorestate *state, void *tup);
@@ -262,6 +264,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes)
state->truncated = false;
state->allowedMem = maxKBytes * 1024L;
state->availMem = state->allowedMem;
+ state->maxSpace = 0;
state->myfile = NULL;
state->context = CurrentMemoryContext;
state->resowner = CurrentResourceOwner;
@@ -420,6 +423,9 @@ tuplestore_clear(Tuplestorestate *state)
int i;
TSReadPointer *readptr;
+ /* update the maxSpace before doing any USEMEM/FREEMEM adjustments */
+ tuplestore_updatemax(state);
+
if (state->myfile)
BufFileClose(state->myfile);
state->myfile = NULL;
@@ -1402,6 +1408,9 @@ tuplestore_trim(Tuplestorestate *state)
Assert(nremove >= state->memtupdeleted);
Assert(nremove <= state->memtupcount);
+ /* before freeing any memory, update maxSpace */
+ tuplestore_updatemax(state);
+
/* Release no-longer-needed tuples */
for (i = state->memtupdeleted; i < nremove; i++)
{
@@ -1445,6 +1454,49 @@ tuplestore_trim(Tuplestorestate *state)
}
/*
+ * tuplestore_updatemax
+ * Update maxSpace field
+ */
+static void
+tuplestore_updatemax(Tuplestorestate *state)
+{
+ if (state->status == TSS_INMEM)
+ state->maxSpace = Max(state->maxSpace,
+ state->allowedMem - state->availMem);
+}
+
+/*
+ * tuplestore_storage_type_name
+ * Return a string description of the storage method used to store the
+ * tuples.
+ */
+const char *
+tuplestore_storage_type_name(Tuplestorestate *state)
+{
+ if (state->status == TSS_INMEM)
+ return "Memory";
+ else
+ return "Disk";
+}
+
+/*
+ * tuplestore_space_used
+ * Return the maximum space used in memory unless the tuplestore has spilled
+ * to disk, in which case, return the disk space used.
+ */
+int64
+tuplestore_space_used(Tuplestorestate *state)
+{
+ /* First, update the maxSpace field */
+ tuplestore_updatemax(state);
+
+ if (state->status == TSS_INMEM)
+ return state->maxSpace;
+ else
+ return BufFileSize(state->myfile);
+}
+
+/*
* tuplestore_in_memory
*
* Returns true if the tuplestore has not spilled to disk.
@@ -1513,6 +1565,7 @@ writetup_heap(Tuplestorestate *state, void *tup)
if (state->backward) /* need trailing length word? */
BufFileWrite(state->myfile, &tuplen, sizeof(tuplen));
+ /* no need to call tuplestore_updatemax() when not in TSS_INMEM */
FREEMEM(state, GetMemoryChunkSpace(tuple));
heap_free_minimal_tuple(tuple);
}
diff --git a/src/include/utils/tuplestore.h b/src/include/utils/tuplestore.h
index 419613c17ba..3d8a90caaf9 100644
--- a/src/include/utils/tuplestore.h
+++ b/src/include/utils/tuplestore.h
@@ -65,6 +65,10 @@ extern void tuplestore_copy_read_pointer(Tuplestorestate *state,
extern void tuplestore_trim(Tuplestorestate *state);
+extern const char *tuplestore_storage_type_name(Tuplestorestate *state);
+
+extern int64 tuplestore_space_used(Tuplestorestate *state);
+
extern bool tuplestore_in_memory(Tuplestorestate *state);
extern bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 7ca98397aec..7a03b4e3607 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -1,6 +1,24 @@
--
-- Test partitioning planner code
--
+-- Helper function which can be used for masking out portions of EXPLAIN
+-- ANALYZE which could contain information that's not consistent on all
+-- platforms.
+create function explain_analyze(query text) returns setof text
+language plpgsql as
+$$
+declare
+ ln text;
+begin
+ for ln in
+ execute format('explain (analyze, costs off, summary off, timing off) %s',
+ query)
+ loop
+ ln := regexp_replace(ln, 'Maximum Storage: \d+', 'Maximum Storage: N');
+ return next ln;
+ end loop;
+end;
+$$;
-- Force generic plans to be used for all prepared statements in this file.
set plan_cache_mode = force_generic_plan;
create table lp (a char) partition by list (a);
@@ -2826,9 +2844,9 @@ deallocate ab_q5;
deallocate ab_q6;
-- UPDATE on a partition subtree has been seen to have problems.
insert into ab values (1,2);
-explain (analyze, costs off, summary off, timing off)
-update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;
- QUERY PLAN
+select explain_analyze('
+update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;');
+ explain_analyze
-------------------------------------------------------------------------------------------
Update on ab_a1 (actual rows=0 loops=1)
Update on ab_a1_b1 ab_a1_1
@@ -2851,6 +2869,7 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;
-> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1)
Index Cond: (a = 1)
-> Materialize (actual rows=1 loops=1)
+ Storage: Memory Maximum Storage: NkB
-> Append (actual rows=1 loops=1)
-> Bitmap Heap Scan on ab_a1_b1 ab_1 (actual rows=0 loops=1)
Recheck Cond: (a = 1)
@@ -2866,7 +2885,7 @@ update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;
Heap Blocks: exact=1
-> Bitmap Index Scan on ab_a1_b3_a_idx (actual rows=1 loops=1)
Index Cond: (a = 1)
-(36 rows)
+(37 rows)
table ab;
a | b
@@ -2877,9 +2896,9 @@ table ab;
-- Test UPDATE where source relation has run-time pruning enabled
truncate ab;
insert into ab values (1, 1), (1, 2), (1, 3), (2, 1);
-explain (analyze, costs off, summary off, timing off)
-update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);
- QUERY PLAN
+select explain_analyze('
+update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);');
+ explain_analyze
------------------------------------------------------------------------------
Update on ab_a1 (actual rows=0 loops=1)
Update on ab_a1_b1 ab_a1_1
@@ -2893,6 +2912,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);
-> Seq Scan on ab_a1_b2 ab_a1_2 (actual rows=1 loops=1)
-> Seq Scan on ab_a1_b3 ab_a1_3 (actual rows=1 loops=1)
-> Materialize (actual rows=1 loops=3)
+ Storage: Memory Maximum Storage: NkB
-> Append (actual rows=1 loops=1)
-> Seq Scan on ab_a2_b1 ab_a2_1 (actual rows=1 loops=1)
Filter: (b = (InitPlan 1).col1)
@@ -2900,7 +2920,7 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);
Filter: (b = (InitPlan 1).col1)
-> Seq Scan on ab_a2_b3 ab_a2_3 (never executed)
Filter: (b = (InitPlan 1).col1)
-(19 rows)
+(20 rows)
select tableoid::regclass, * from ab;
tableoid | a | b
@@ -4419,3 +4439,4 @@ explain (costs off) select * from hp_contradict_test where a === 1 and b === 1 a
drop table hp_contradict_test;
drop operator class part_test_int4_ops2 using hash;
drop operator ===(int4, int4);
+drop function explain_analyze(text);
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index a09b27d820c..442428d937c 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -2,6 +2,25 @@
-- Test partitioning planner code
--
+-- Helper function which can be used for masking out portions of EXPLAIN
+-- ANALYZE which could contain information that's not consistent on all
+-- platforms.
+create function explain_analyze(query text) returns setof text
+language plpgsql as
+$$
+declare
+ ln text;
+begin
+ for ln in
+ execute format('explain (analyze, costs off, summary off, timing off) %s',
+ query)
+ loop
+ ln := regexp_replace(ln, 'Maximum Storage: \d+', 'Maximum Storage: N');
+ return next ln;
+ end loop;
+end;
+$$;
+
-- Force generic plans to be used for all prepared statements in this file.
set plan_cache_mode = force_generic_plan;
@@ -676,15 +695,15 @@ deallocate ab_q6;
-- UPDATE on a partition subtree has been seen to have problems.
insert into ab values (1,2);
-explain (analyze, costs off, summary off, timing off)
-update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;
+select explain_analyze('
+update ab_a1 set b = 3 from ab where ab.a = 1 and ab.a = ab_a1.a;');
table ab;
-- Test UPDATE where source relation has run-time pruning enabled
truncate ab;
insert into ab values (1, 1), (1, 2), (1, 3), (2, 1);
-explain (analyze, costs off, summary off, timing off)
-update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);
+select explain_analyze('
+update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1);');
select tableoid::regclass, * from ab;
drop table ab, lprt_a;
@@ -1318,3 +1337,5 @@ explain (costs off) select * from hp_contradict_test where a === 1 and b === 1 a
drop table hp_contradict_test;
drop operator class part_test_int4_ops2 using hash;
drop operator ===(int4, int4);
+
+drop function explain_analyze(text);