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);
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);
}
}
+/*
+ * 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.
*/
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 */
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);
state->truncated = false;
state->allowedMem = maxKBytes * 1024L;
state->availMem = state->allowedMem;
+ state->maxSpace = 0;
state->myfile = NULL;
state->context = CurrentMemoryContext;
state->resowner = CurrentResourceOwner;
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;
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++)
{
}
}
+/*
+ * 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
*
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);
}
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,
--
-- 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);
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
-> 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)
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
-- 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
-> 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)
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
drop table hp_contradict_test;
drop operator class part_test_int4_ops2 using hash;
drop operator ===(int4, int4);
+drop function explain_analyze(text);
-- 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;
-- 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;
drop table hp_contradict_test;
drop operator class part_test_int4_ops2 using hash;
drop operator ===(int4, int4);
+
+drop function explain_analyze(text);