Skip to content

Commit a5495cd

Browse files
committed
Add a hook to let loadable modules get control at ProcessUtility execution,
and use it to extend contrib/pg_stat_statements to track utility commands. Itagaki Takahiro, reviewed by Euler Taveira de Oliveira.
1 parent 34d2687 commit a5495cd

File tree

4 files changed

+135
-11
lines changed

4 files changed

+135
-11
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Copyright (c) 2008-2009, PostgreSQL Global Development Group
1515
*
1616
* IDENTIFICATION
17-
* $PostgreSQL: pgsql/contrib/pg_stat_statements/pg_stat_statements.c,v 1.8 2009/12/15 04:57:46 rhaas Exp $
17+
* $PostgreSQL: pgsql/contrib/pg_stat_statements/pg_stat_statements.c,v 1.9 2009/12/15 20:04:49 tgl Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -32,6 +32,7 @@
3232
#include "storage/fd.h"
3333
#include "storage/ipc.h"
3434
#include "storage/spin.h"
35+
#include "tcop/utility.h"
3536
#include "utils/builtins.h"
3637
#include "utils/hsearch.h"
3738
#include "utils/guc.h"
@@ -113,6 +114,7 @@ static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
113114
static ExecutorStart_hook_type prev_ExecutorStart = NULL;
114115
static ExecutorRun_hook_type prev_ExecutorRun = NULL;
115116
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
117+
static ProcessUtility_hook_type prev_ProcessUtility = NULL;
116118

117119
/* Links to shared memory state */
118120
static pgssSharedState *pgss = NULL;
@@ -124,7 +126,7 @@ typedef enum
124126
{
125127
PGSS_TRACK_NONE, /* track no statements */
126128
PGSS_TRACK_TOP, /* only top level statements */
127-
PGSS_TRACK_ALL, /* all statements, including nested ones */
129+
PGSS_TRACK_ALL /* all statements, including nested ones */
128130
} PGSSTrackLevel;
129131

130132
static const struct config_enum_entry track_options[] = {
@@ -136,6 +138,7 @@ static const struct config_enum_entry track_options[] = {
136138

137139
static int pgss_max; /* max # statements to track */
138140
static int pgss_track; /* tracking level */
141+
static bool pgss_track_utility; /* whether to track utility commands */
139142
static bool pgss_save; /* whether to save stats across shutdown */
140143

141144

@@ -161,10 +164,12 @@ static void pgss_ExecutorRun(QueryDesc *queryDesc,
161164
ScanDirection direction,
162165
long count);
163166
static void pgss_ExecutorEnd(QueryDesc *queryDesc);
167+
static void pgss_ProcessUtility(Node *parsetree,
168+
const char *queryString, ParamListInfo params, bool isTopLevel,
169+
DestReceiver *dest, char *completionTag);
164170
static uint32 pgss_hash_fn(const void *key, Size keysize);
165171
static int pgss_match_fn(const void *key1, const void *key2, Size keysize);
166-
static void pgss_store(const char *query,
167-
const Instrumentation *instr, uint32 rows);
172+
static void pgss_store(const char *query, double total_time, uint64 rows);
168173
static Size pgss_memsize(void);
169174
static pgssEntry *entry_alloc(pgssHashKey *key);
170175
static void entry_dealloc(void);
@@ -214,6 +219,16 @@ _PG_init(void)
214219
NULL,
215220
NULL);
216221

222+
DefineCustomBoolVariable("pg_stat_statements.track_utility",
223+
"Selects whether utility commands are tracked by pg_stat_statements.",
224+
NULL,
225+
&pgss_track_utility,
226+
true,
227+
PGC_SUSET,
228+
0,
229+
NULL,
230+
NULL);
231+
217232
DefineCustomBoolVariable("pg_stat_statements.save",
218233
"Save pg_stat_statements statistics across server shutdowns.",
219234
NULL,
@@ -245,6 +260,8 @@ _PG_init(void)
245260
ExecutorRun_hook = pgss_ExecutorRun;
246261
prev_ExecutorEnd = ExecutorEnd_hook;
247262
ExecutorEnd_hook = pgss_ExecutorEnd;
263+
prev_ProcessUtility = ProcessUtility_hook;
264+
ProcessUtility_hook = pgss_ProcessUtility;
248265
}
249266

250267
/*
@@ -254,10 +271,11 @@ void
254271
_PG_fini(void)
255272
{
256273
/* Uninstall hooks. */
274+
shmem_startup_hook = prev_shmem_startup_hook;
257275
ExecutorStart_hook = prev_ExecutorStart;
258276
ExecutorRun_hook = prev_ExecutorRun;
259277
ExecutorEnd_hook = prev_ExecutorEnd;
260-
shmem_startup_hook = prev_shmem_startup_hook;
278+
ProcessUtility_hook = prev_ProcessUtility;
261279
}
262280

263281
/*
@@ -539,7 +557,7 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
539557
InstrEndLoop(queryDesc->totaltime);
540558

541559
pgss_store(queryDesc->sourceText,
542-
queryDesc->totaltime,
560+
queryDesc->totaltime->total,
543561
queryDesc->estate->es_processed);
544562
}
545563

@@ -549,6 +567,61 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
549567
standard_ExecutorEnd(queryDesc);
550568
}
551569

570+
/*
571+
* ProcessUtility hook
572+
*/
573+
static void
574+
pgss_ProcessUtility(Node *parsetree, const char *queryString,
575+
ParamListInfo params, bool isTopLevel,
576+
DestReceiver *dest, char *completionTag)
577+
{
578+
if (pgss_track_utility && pgss_enabled())
579+
{
580+
instr_time start;
581+
instr_time duration;
582+
uint64 rows = 0;
583+
584+
INSTR_TIME_SET_CURRENT(start);
585+
586+
nested_level++;
587+
PG_TRY();
588+
{
589+
if (prev_ProcessUtility)
590+
prev_ProcessUtility(parsetree, queryString, params,
591+
isTopLevel, dest, completionTag);
592+
else
593+
standard_ProcessUtility(parsetree, queryString, params,
594+
isTopLevel, dest, completionTag);
595+
nested_level--;
596+
}
597+
PG_CATCH();
598+
{
599+
nested_level--;
600+
PG_RE_THROW();
601+
}
602+
PG_END_TRY();
603+
604+
INSTR_TIME_SET_CURRENT(duration);
605+
INSTR_TIME_SUBTRACT(duration, start);
606+
607+
/* parse command tag to retrieve the number of affected rows. */
608+
if (completionTag &&
609+
sscanf(completionTag, "COPY " UINT64_FORMAT, &rows) != 1)
610+
rows = 0;
611+
612+
pgss_store(queryString, INSTR_TIME_GET_DOUBLE(duration), rows);
613+
}
614+
else
615+
{
616+
if (prev_ProcessUtility)
617+
prev_ProcessUtility(parsetree, queryString, params,
618+
isTopLevel, dest, completionTag);
619+
else
620+
standard_ProcessUtility(parsetree, queryString, params,
621+
isTopLevel, dest, completionTag);
622+
}
623+
}
624+
552625
/*
553626
* Calculate hash value for a key
554627
*/
@@ -587,7 +660,7 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
587660
* Store some statistics for a statement.
588661
*/
589662
static void
590-
pgss_store(const char *query, const Instrumentation *instr, uint32 rows)
663+
pgss_store(const char *query, double total_time, uint64 rows)
591664
{
592665
pgssHashKey key;
593666
double usage;
@@ -631,7 +704,7 @@ pgss_store(const char *query, const Instrumentation *instr, uint32 rows)
631704

632705
SpinLockAcquire(&e->mutex);
633706
e->counters.calls += 1;
634-
e->counters.total_time += instr->total;
707+
e->counters.total_time += total_time;
635708
e->counters.rows += rows;
636709
e->counters.usage += usage;
637710
SpinLockRelease(&e->mutex);

doc/src/sgml/pgstatstatements.sgml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/pgstatstatements.sgml,v 1.4 2009/12/01 02:31:11 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/pgstatstatements.sgml,v 1.5 2009/12/15 20:04:49 tgl Exp $ -->
22

33
<sect1 id="pgstatstatements">
44
<title>pg_stat_statements</title>
@@ -174,6 +174,23 @@
174174
</listitem>
175175
</varlistentry>
176176

177+
<varlistentry>
178+
<term>
179+
<varname>pg_stat_statements.track_utility</varname> (<type>boolean</type>)
180+
</term>
181+
182+
<listitem>
183+
<para>
184+
<varname>pg_stat_statements.track_utility</varname> controls whether
185+
utility commands are tracked by the module. Utility commands are
186+
all those other than <command>SELECT</>, <command>INSERT</>,
187+
<command>UPDATE</> and <command>DELETE</>.
188+
The default value is <literal>on</>.
189+
Only superusers can change this setting.
190+
</para>
191+
</listitem>
192+
</varlistentry>
193+
177194
<varlistentry>
178195
<term>
179196
<varname>pg_stat_statements.save</varname> (<type>boolean</type>)

src/backend/tcop/utility.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.323 2009/12/11 03:34:55 itagaki Exp $
13+
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.324 2009/12/15 20:04:49 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -58,6 +58,10 @@
5858
#include "utils/syscache.h"
5959

6060

61+
/* Hook for plugins to get control in ProcessUtility() */
62+
ProcessUtility_hook_type ProcessUtility_hook = NULL;
63+
64+
6165
/*
6266
* Verify user has ownership of specified relation, else ereport.
6367
*
@@ -274,6 +278,27 @@ ProcessUtility(Node *parsetree,
274278
{
275279
Assert(queryString != NULL); /* required as of 8.4 */
276280

281+
/*
282+
* We provide a function hook variable that lets loadable plugins
283+
* get control when ProcessUtility is called. Such a plugin would
284+
* normally call standard_ProcessUtility().
285+
*/
286+
if (ProcessUtility_hook)
287+
(*ProcessUtility_hook) (parsetree, queryString, params,
288+
isTopLevel, dest, completionTag);
289+
else
290+
standard_ProcessUtility(parsetree, queryString, params,
291+
isTopLevel, dest, completionTag);
292+
}
293+
294+
void
295+
standard_ProcessUtility(Node *parsetree,
296+
const char *queryString,
297+
ParamListInfo params,
298+
bool isTopLevel,
299+
DestReceiver *dest,
300+
char *completionTag)
301+
{
277302
check_xact_readonly(parsetree);
278303

279304
if (completionTag)

src/include/tcop/utility.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.37 2009/12/01 02:31:13 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.38 2009/12/15 20:04:49 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -17,9 +17,18 @@
1717
#include "tcop/tcopprot.h"
1818

1919

20+
/* Hook for plugins to get control in ProcessUtility() */
21+
typedef void (*ProcessUtility_hook_type) (Node *parsetree,
22+
const char *queryString, ParamListInfo params, bool isTopLevel,
23+
DestReceiver *dest, char *completionTag);
24+
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
25+
2026
extern void ProcessUtility(Node *parsetree, const char *queryString,
2127
ParamListInfo params, bool isTopLevel,
2228
DestReceiver *dest, char *completionTag);
29+
extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
30+
ParamListInfo params, bool isTopLevel,
31+
DestReceiver *dest, char *completionTag);
2332

2433
extern bool UtilityReturnsTuples(Node *parsetree);
2534

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy