Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 3 additions & 19 deletions contrib/auto_explain/auto_explain.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,19 +305,9 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)

if (auto_explain_enabled())
{
/*
* Set up to track total elapsed time in ExecutorRun. Make sure the
* space is allocated in the per-query context so it will go away at
* ExecutorEnd.
*/
/* Set up to track total elapsed time in ExecutorRun. */
if (queryDesc->totaltime == NULL)
{
MemoryContext oldcxt;

oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false);
MemoryContextSwitchTo(oldcxt);
}
queryDesc->totaltime = InstrQueryAlloc(INSTRUMENT_ALL);
}
}

Expand Down Expand Up @@ -381,14 +371,8 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
*/
oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);

/*
* Make sure stats accumulation is done. (Note: it's okay if several
* levels of hook all do this.)
*/
InstrEndLoop(queryDesc->totaltime);

/* Log plan if duration is exceeded. */
msec = INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->total);
msec = INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->instr.total);
if (msec >= auto_explain_log_min_duration)
{
ExplainState *es = NewExplainState();
Expand Down
70 changes: 70 additions & 0 deletions contrib/pg_stat_statements/expected/utility.out
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,76 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)

-- Buffer stats should flow through EXPLAIN ANALYZE
CREATE TEMP TABLE flow_through_test (a int, b char(200));
INSERT INTO flow_through_test SELECT i, repeat('x', 200) FROM generate_series(1, 5000) AS i;
CREATE FUNCTION run_explain_buffers_test() RETURNS void AS $$
DECLARE
BEGIN
EXECUTE 'EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM flow_through_test';
END;
$$ LANGUAGE plpgsql;
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)

SELECT run_explain_buffers_test();
run_explain_buffers_test
--------------------------

(1 row)

-- EXPLAIN entries should have non-zero buffer stats
SELECT query, local_blks_hit + local_blks_read > 0 as has_buffer_stats
FROM pg_stat_statements
WHERE query LIKE 'SELECT run_explain_buffers_test%'
ORDER BY query COLLATE "C";
query | has_buffer_stats
-----------------------------------+------------------
SELECT run_explain_buffers_test() | t
(1 row)

DROP FUNCTION run_explain_buffers_test;
DROP TABLE flow_through_test;
-- Validate buffer/WAL counting during abort
SET pg_stat_statements.track = 'all';
CREATE TEMP TABLE pgss_call_tab (a int, b char(20));
CREATE TEMP TABLE pgss_call_tab2 (a int, b char(20));
INSERT INTO pgss_call_tab VALUES (0, 'zzz');
CREATE PROCEDURE pgss_call_rollback_proc() AS $$
DECLARE
v int;
BEGIN
EXPLAIN ANALYZE WITH ins AS (INSERT INTO pgss_call_tab2 SELECT * FROM pgss_call_tab RETURNING a)
SELECT a / 0 INTO v FROM ins;
EXCEPTION WHEN division_by_zero THEN
END;
$$ LANGUAGE plpgsql;
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)

CALL pgss_call_rollback_proc();
SELECT query, calls,
local_blks_hit + local_blks_read > 0 as local_hitread,
wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated
FROM pg_stat_statements
WHERE query LIKE '%pgss_call_rollback_proc%'
ORDER BY query COLLATE "C";
query | calls | local_hitread | wal_bytes_generated | wal_records_generated
--------------------------------+-------+---------------+---------------------+-----------------------
CALL pgss_call_rollback_proc() | 1 | t | t | t
(1 row)

DROP TABLE pgss_call_tab2;
DROP TABLE pgss_call_tab;
DROP PROCEDURE pgss_call_rollback_proc;
SET pg_stat_statements.track = 'top';
-- CALL
CREATE OR REPLACE PROCEDURE sum_one(i int) AS $$
DECLARE
Expand Down
48 changes: 48 additions & 0 deletions contrib/pg_stat_statements/expected/wal.out
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,51 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
(1 row)

--
-- Validate buffer/WAL counting with caught exception in PL/pgSQL
--
CREATE TEMP TABLE pgss_error_tab (a int, b char(20));
INSERT INTO pgss_error_tab VALUES (0, 'zzz');
CREATE FUNCTION pgss_error_func() RETURNS void AS $$
DECLARE
v int;
BEGIN
WITH ins AS (INSERT INTO pgss_error_tab VALUES (1, 'aaa') RETURNING a)
SELECT a / 0 INTO v FROM ins;
EXCEPTION WHEN division_by_zero THEN
NULL;
END;
$$ LANGUAGE plpgsql;
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)

SELECT pgss_error_func();
pgss_error_func
-----------------

(1 row)

-- Buffer/WAL usage from the wCTE INSERT should survive the exception
SELECT query, calls,
local_blks_hit + local_blks_read > 0 as local_hitread,
wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated
FROM pg_stat_statements
WHERE query LIKE '%pgss_error_func%'
ORDER BY query COLLATE "C";
query | calls | local_hitread | wal_bytes_generated | wal_records_generated
--------------------------+-------+---------------+---------------------+-----------------------
SELECT pgss_error_func() | 1 | t | t | t
(1 row)

DROP TABLE pgss_error_tab;
DROP FUNCTION pgss_error_func;
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)

93 changes: 20 additions & 73 deletions contrib/pg_stat_statements/pg_stat_statements.c
Original file line number Diff line number Diff line change
Expand Up @@ -911,22 +911,11 @@ pgss_planner(Query *parse,
&& pgss_track_planning && query_string
&& parse->queryId != INT64CONST(0))
{
instr_time start;
instr_time duration;
BufferUsage bufusage_start,
bufusage;
WalUsage walusage_start,
walusage;
Instrumentation instr = {0};

/* We need to track buffer usage as the planner can access them. */
bufusage_start = pgBufferUsage;

/*
* Similarly the planner could write some WAL records in some cases
* (e.g. setting a hint bit with those being WAL-logged)
*/
walusage_start = pgWalUsage;
INSTR_TIME_SET_CURRENT(start);
/* Track time and buffer/WAL usage as the planner can access them. */
InstrInitOptions(&instr, INSTRUMENT_ALL);
InstrStart(&instr);

nesting_level++;
PG_TRY();
Expand All @@ -940,30 +929,20 @@ pgss_planner(Query *parse,
}
PG_FINALLY();
{
InstrStopFinalize(&instr);
nesting_level--;
}
PG_END_TRY();

INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);

/* calc differences of buffer counters. */
memset(&bufusage, 0, sizeof(BufferUsage));
BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);

/* calc differences of WAL counters. */
memset(&walusage, 0, sizeof(WalUsage));
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);

pgss_store(query_string,
parse->queryId,
parse->stmt_location,
parse->stmt_len,
PGSS_PLAN,
INSTR_TIME_GET_MILLISEC(duration),
INSTR_TIME_GET_MILLISEC(instr.total),
0,
&bufusage,
&walusage,
&instr.bufusage,
&instr.walusage,
NULL,
NULL,
0,
Expand Down Expand Up @@ -1015,19 +994,9 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
if (pgss_enabled(nesting_level) && queryDesc->plannedstmt->queryId != INT64CONST(0))
{
/*
* Set up to track total elapsed time in ExecutorRun. Make sure the
* space is allocated in the per-query context so it will go away at
* ExecutorEnd.
*/
/* Set up to track total elapsed time in ExecutorRun. */
if (queryDesc->totaltime == NULL)
{
MemoryContext oldcxt;

oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false);
MemoryContextSwitchTo(oldcxt);
}
queryDesc->totaltime = InstrQueryAlloc(INSTRUMENT_ALL);
}
}

Expand Down Expand Up @@ -1084,21 +1053,15 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
if (queryId != INT64CONST(0) && queryDesc->totaltime &&
pgss_enabled(nesting_level))
{
/*
* Make sure stats accumulation is done. (Note: it's okay if several
* levels of hook all do this.)
*/
InstrEndLoop(queryDesc->totaltime);

pgss_store(queryDesc->sourceText,
queryId,
queryDesc->plannedstmt->stmt_location,
queryDesc->plannedstmt->stmt_len,
PGSS_EXEC,
INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->total),
INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->instr.total),
queryDesc->estate->es_total_processed,
&queryDesc->totaltime->bufusage,
&queryDesc->totaltime->walusage,
&queryDesc->totaltime->instr.bufusage,
&queryDesc->totaltime->instr.walusage,
queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
NULL,
queryDesc->estate->es_parallel_workers_to_launch,
Expand Down Expand Up @@ -1162,17 +1125,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
!IsA(parsetree, ExecuteStmt) &&
!IsA(parsetree, PrepareStmt))
{
instr_time start;
instr_time duration;
uint64 rows;
BufferUsage bufusage_start,
bufusage;
WalUsage walusage_start,
walusage;
Instrumentation instr = {0};

bufusage_start = pgBufferUsage;
walusage_start = pgWalUsage;
INSTR_TIME_SET_CURRENT(start);
InstrInitOptions(&instr, INSTRUMENT_ALL);
InstrStart(&instr);

nesting_level++;
PG_TRY();
Expand All @@ -1188,6 +1145,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
}
PG_FINALLY();
{
InstrStopFinalize(&instr);
nesting_level--;
}
PG_END_TRY();
Expand All @@ -1202,9 +1160,6 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* former value, which'd otherwise be a good idea.
*/

INSTR_TIME_SET_CURRENT(duration);
INSTR_TIME_SUBTRACT(duration, start);

/*
* Track the total number of rows retrieved or affected by the utility
* statements of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED
Expand All @@ -1216,23 +1171,15 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
qc->nprocessed : 0;

/* calc differences of buffer counters. */
memset(&bufusage, 0, sizeof(BufferUsage));
BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);

/* calc differences of WAL counters. */
memset(&walusage, 0, sizeof(WalUsage));
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);

pgss_store(queryString,
saved_queryId,
saved_stmt_location,
saved_stmt_len,
PGSS_EXEC,
INSTR_TIME_GET_MILLISEC(duration),
INSTR_TIME_GET_MILLISEC(instr.total),
rows,
&bufusage,
&walusage,
&instr.bufusage,
&instr.walusage,
NULL,
NULL,
0,
Expand Down
Loading