-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpg_relusage.c
More file actions
154 lines (131 loc) · 3.99 KB
/
pg_relusage.c
File metadata and controls
154 lines (131 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*-------------------------------------------------------------------------
*
* pg_relusage is a PostgreSQL extension which allows to log relations that were
* used by the current statement.
*
* This code is based on Bruce Momijan lectures and auto_explain.
*
* This program is open source, licensed under the PostgreSQL license.
* For license terms, see the LICENSE file.
*
* Copyright (c) 2022, Dmitry Astapov <dastapov@gmail.com>
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/executor.h"
#include "utils/lsyscache.h"
#include "nodes/pg_list.h"
#include "utils/guc.h"
#if PG_VERSION_NUM >= 90600
#include "access/parallel.h"
#endif
PG_MODULE_MAGIC;
static int pg_relusage_log_level = LOG;
static const struct config_enum_entry log_level_options[] = {
{"debug5", DEBUG5, false},
{"debug4", DEBUG4, false},
{"debug3", DEBUG3, false},
{"debug2", DEBUG2, false},
{"debug1", DEBUG1, false},
{"debug", DEBUG2, true},
{"info", INFO, false},
{"notice", NOTICE, false},
{"warning", WARNING, false},
{"log", LOG, false},
{NULL, 0, false}
};
static char* pg_relusage_rel_kinds = "riSvmfp";
static ExecutorStart_hook_type prev_ExecutorStart = NULL;
void _PG_init(void);
void _PG_fini(void);
static void pg_relusage_ExecutorStart(QueryDesc *queryDesc, int eflags);
void
_PG_init(void)
{
DefineCustomEnumVariable("pg_relusage.log_level",
"Log level for pg_relusage.",
NULL,
&pg_relusage_log_level,
LOG,
log_level_options,
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomStringVariable("pg_relusage.rel_kinds",
"rel_kinds that pg_relusage will report",
"pg_class.relkind of the relation will be checked again this string, and if there is a match, relation will be reported",
&pg_relusage_rel_kinds,
"riSvmfp",
PGC_SUSET,
0,
NULL,
NULL,
NULL);
EmitWarningsOnPlaceholders("pg_relusage");
/* Install hooks only on leader. */
#if PG_VERSION_NUM >= 90600
if (!IsParallelWorker())
{
#endif
prev_ExecutorStart = ExecutorStart_hook;
ExecutorStart_hook = pg_relusage_ExecutorStart;
#if PG_VERSION_NUM >= 90600
}
#endif
}
void
_PG_fini(void)
{
#if PG_VERSION_NUM >= 90600
if (!IsParallelWorker())
{
#endif
ExecutorStart_hook = prev_ExecutorStart;
#if PG_VERSION_NUM >= 90600
}
#endif
}
static void
pg_relusage_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
ListCell *lst;
ListCell *lst2;
Oid oid;
StringInfoData buf;
bool buf_empty=true;
char kind;
initStringInfo(&buf);
foreach(lst, queryDesc -> plannedstmt -> relationOids) {
bool seen = false;
oid = lfirst_oid(lst);
kind = get_rel_relkind(oid);
// Exclude things with unknown relkind, and ignore indices and TOAST tables
if (!kind || !strchr(pg_relusage_rel_kinds, kind) ) continue;
// Oid list is not deduplicated so we check if the same oid was mentioned before
foreach(lst2, queryDesc -> plannedstmt -> relationOids) {
if(lst2 == lst)
break;
if(oid == lfirst_oid(lst2))
seen = true;
}
if (!seen) {
if (buf_empty) {
buf_empty=false;
} else {
appendStringInfoChar(&buf, ',');
};
appendStringInfoString(&buf, get_rel_name(oid));
}
};
if (!buf_empty) {
ereport(pg_relusage_log_level, (errmsg("relations used: %s", buf.data), errhidestmt(true)));
};
pfree(buf.data);
if (prev_ExecutorStart)
prev_ExecutorStart(queryDesc, eflags);
else
standard_ExecutorStart(queryDesc, eflags);
}