pg_peek 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/pg_peek/application.css +13 -0
- data/app/controllers/pg_peek/pg_stat_statements_controller.rb +9 -3
- data/app/models/pg_peek/pg_stat_statements.rb +53 -3
- data/app/views/layouts/pg_peek/application.html.erb +1 -0
- data/app/views/pg_peek/databases/show.html.erb +8 -0
- data/app/views/pg_peek/pg_stat_statements/_preload_instructions.html.erb +5 -1
- data/lib/pg_peek/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb99f16f0d2715eb1e038f98b4c7a43077678408b4f7f216f1238a823540f448
|
|
4
|
+
data.tar.gz: 2bda074f80788e6ab201e49948ce76539cb2e62ce2d0f06276ec22262ed32fda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cbd81cbdb7abff990c656557f38e5d889e96c4b80a632a9c55d48b49b0363dd3443ee3ca13adc8ced93edcd5fde0b9c5fdeb4d202f8f64a755aeebacbcffe37d
|
|
7
|
+
data.tar.gz: 0f60578fbe2b529b6fc076d087a2908a0806e23f77597e70d57a8abc8166e8aa89509805f493af20a7495aba75c683a395ec4af1e1e6b032891491d20ae7b1c5
|
|
@@ -280,6 +280,12 @@ button, .button {
|
|
|
280
280
|
button:hover { border-color: var(--fg); }
|
|
281
281
|
button.danger { color: var(--danger); border-color: var(--danger); }
|
|
282
282
|
button.danger:hover { background: var(--danger); color: var(--bg); }
|
|
283
|
+
button:disabled, button:disabled:hover {
|
|
284
|
+
background: var(--panel);
|
|
285
|
+
color: var(--dim);
|
|
286
|
+
border-color: var(--line);
|
|
287
|
+
cursor: not-allowed;
|
|
288
|
+
}
|
|
283
289
|
|
|
284
290
|
form.inline { display: inline; margin: 0; }
|
|
285
291
|
|
|
@@ -297,6 +303,13 @@ form.inline { display: inline; margin: 0; }
|
|
|
297
303
|
margin: 0.75rem 0;
|
|
298
304
|
}
|
|
299
305
|
|
|
306
|
+
.alert {
|
|
307
|
+
border: 1px solid var(--danger);
|
|
308
|
+
color: var(--danger);
|
|
309
|
+
padding: 0.4rem 0.75rem;
|
|
310
|
+
margin: 0.75rem 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
300
313
|
mark { background: var(--accent); color: var(--bg); }
|
|
301
314
|
|
|
302
315
|
.grid {
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
class PgPeek::PgStatStatementsController < PgPeek::ApplicationController
|
|
4
4
|
def reset
|
|
5
5
|
@database = PgPeek::Database.find(params[:database_id]) or raise ActiveRecord::RecordNotFound, "Database not found"
|
|
6
|
-
PgPeek::PgStatStatements.new(database: @database)
|
|
6
|
+
pg_stat_statements = PgPeek::PgStatStatements.new(database: @database)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
if pg_stat_statements.resettable?
|
|
9
|
+
pg_stat_statements.reset!
|
|
10
|
+
flash[:notice] = "pg_stat_statements has been reset."
|
|
11
|
+
else
|
|
12
|
+
flash[:alert] = "This role may not reset pg_stat_statements; the statistics were left as they are."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
redirect_to database_path(@database, anchor: "pg_stat_statements")
|
|
10
16
|
end
|
|
11
17
|
end
|
|
@@ -24,16 +24,34 @@ class PgPeek::PgStatStatements
|
|
|
24
24
|
# The extension can be created in the database while the module itself was
|
|
25
25
|
# never loaded at server start. Querying the view then raises
|
|
26
26
|
# PG::ObjectNotInPrerequisiteState, so every read has to check this first.
|
|
27
|
+
#
|
|
28
|
+
# Roles without pg_read_all_settings (dokku's postgres plugin, most managed
|
|
29
|
+
# hosts) cannot read shared_preload_libraries, so then the only way to tell
|
|
30
|
+
# is to ask the module itself.
|
|
27
31
|
def preloaded?
|
|
28
|
-
shared_preload_libraries
|
|
32
|
+
if shared_preload_libraries
|
|
33
|
+
shared_preload_libraries.split(",").map(&:strip).include?(LIBRARY_NAME)
|
|
34
|
+
else
|
|
35
|
+
installed? && module_loaded?
|
|
36
|
+
end
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
def usable?
|
|
32
40
|
installed? && preloaded?
|
|
33
41
|
end
|
|
34
42
|
|
|
43
|
+
# nil when this role may not examine the setting, as opposed to "" when
|
|
44
|
+
# nothing is preloaded.
|
|
35
45
|
def shared_preload_libraries
|
|
36
|
-
@shared_preload_libraries
|
|
46
|
+
return @shared_preload_libraries if defined?(@shared_preload_libraries)
|
|
47
|
+
|
|
48
|
+
@shared_preload_libraries = without_aborting_transaction do
|
|
49
|
+
connection.select_value(PgPeek::QueryLoader.mark("SHOW shared_preload_libraries")).to_s
|
|
50
|
+
end
|
|
51
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
52
|
+
raise unless e.cause.is_a?(PG::InsufficientPrivilege)
|
|
53
|
+
|
|
54
|
+
@shared_preload_libraries = nil
|
|
37
55
|
end
|
|
38
56
|
|
|
39
57
|
def outdated?
|
|
@@ -42,12 +60,23 @@ class PgPeek::PgStatStatements
|
|
|
42
60
|
@installed_version != @default_version
|
|
43
61
|
end
|
|
44
62
|
|
|
63
|
+
# The extension revokes EXECUTE on the reset from PUBLIC, so only superusers
|
|
64
|
+
# and roles granted it may run one.
|
|
65
|
+
def resettable?
|
|
66
|
+
return @resettable if defined?(@resettable)
|
|
67
|
+
|
|
68
|
+
@resettable = usable? && connection.select_value(PgPeek::QueryLoader.mark(<<~SQL)) == true
|
|
69
|
+
SELECT bool_and(has_function_privilege(oid, 'EXECUTE'))
|
|
70
|
+
FROM pg_proc WHERE proname = 'pg_stat_statements_reset'
|
|
71
|
+
SQL
|
|
72
|
+
end
|
|
73
|
+
|
|
45
74
|
# Server-wide on purpose. pg_stat_statements_info keeps a single stats_reset
|
|
46
75
|
# for the whole server and only moves it when every entry is removed, so a
|
|
47
76
|
# reset scoped to this database would leave "stats since" pointing at the
|
|
48
77
|
# old time.
|
|
49
78
|
def reset!
|
|
50
|
-
return unless
|
|
79
|
+
return unless resettable?
|
|
51
80
|
|
|
52
81
|
connection.execute PgPeek::QueryLoader.mark(<<-SQL)
|
|
53
82
|
SELECT pg_stat_statements_reset();
|
|
@@ -119,6 +148,27 @@ class PgPeek::PgStatStatements
|
|
|
119
148
|
|
|
120
149
|
private
|
|
121
150
|
|
|
151
|
+
# pg_stat_statements_info() raises PG::ObjectNotInPrerequisiteState unless
|
|
152
|
+
# the module was loaded at server start, and is readable by any role.
|
|
153
|
+
def module_loaded?
|
|
154
|
+
return @module_loaded if defined?(@module_loaded)
|
|
155
|
+
|
|
156
|
+
@module_loaded = without_aborting_transaction do
|
|
157
|
+
connection.select_value(PgPeek::QueryLoader.mark("SELECT 1 FROM pg_stat_statements_info"))
|
|
158
|
+
true
|
|
159
|
+
end
|
|
160
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
161
|
+
raise unless e.cause.is_a?(PG::ObjectNotInPrerequisiteState)
|
|
162
|
+
|
|
163
|
+
@module_loaded = false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# A failed statement inside an open transaction (a host app's, or a
|
|
167
|
+
# transactional test) would poison it; a savepoint confines the failure.
|
|
168
|
+
def without_aborting_transaction(&)
|
|
169
|
+
connection.transaction(requires_new: true, &)
|
|
170
|
+
end
|
|
171
|
+
|
|
122
172
|
def verify_installation
|
|
123
173
|
result = connection.execute PgPeek::QueryLoader.mark(<<-SQL)
|
|
124
174
|
SELECT default_version, installed_version FROM pg_available_extensions WHERE name = 'pg_stat_statements';
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<body>
|
|
16
16
|
<main>
|
|
17
17
|
<% if flash[:notice] %><p class="notice"><%= flash[:notice] %></p><% end %>
|
|
18
|
+
<% if flash[:alert] %><p class="alert"><%= flash[:alert] %></p><% end %>
|
|
18
19
|
<nav>
|
|
19
20
|
<ul>
|
|
20
21
|
<li><%= link_to "pg_peek", databases_path, class: "wordmark" %></li>
|
|
@@ -142,8 +142,16 @@ end</code></pre>
|
|
|
142
142
|
reset_database_pg_stat_statements_path(@database),
|
|
143
143
|
method: :delete,
|
|
144
144
|
class: "danger",
|
|
145
|
+
disabled: !@pg_stat_statements.resettable?,
|
|
145
146
|
form_class: "inline",
|
|
146
147
|
form: { data: { confirm: "Reset pg_stat_statements? This clears statement statistics for every database on this server." } } %>
|
|
148
|
+
<% unless @pg_stat_statements.resettable? %>
|
|
149
|
+
<p class="dim">
|
|
150
|
+
<%= @connection_summary["username"] %> may not reset the statistics: the extension
|
|
151
|
+
allows it only to superusers and roles granted it. To allow it:
|
|
152
|
+
</p>
|
|
153
|
+
<pre><code>GRANT EXECUTE ON FUNCTION pg_stat_statements_reset TO <%= @connection_summary["username"] %>;</code></pre>
|
|
154
|
+
<% end %>
|
|
147
155
|
<% if @pg_stat_statements.outdated? %>
|
|
148
156
|
<p class="dim">
|
|
149
157
|
Version <%= @pg_stat_statements.default_version %> is available:
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
<footer>
|
|
12
12
|
<small>
|
|
13
13
|
A server restart is required — this setting cannot be reloaded.
|
|
14
|
-
|
|
14
|
+
<% if pg_stat_statements.shared_preload_libraries %>
|
|
15
|
+
Currently loaded: <code><%= pg_stat_statements.shared_preload_libraries.presence || "(none)" %></code>
|
|
16
|
+
<% else %>
|
|
17
|
+
This role may not read <code>shared_preload_libraries</code>; the module itself reported it is not loaded.
|
|
18
|
+
<% end %>
|
|
15
19
|
</small>
|
|
16
20
|
</footer>
|
|
17
21
|
</article>
|
data/lib/pg_peek/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pg_peek
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tomasz Mazur
|
|
@@ -171,6 +171,7 @@ licenses:
|
|
|
171
171
|
metadata:
|
|
172
172
|
homepage_uri: https://github.com/trix/pg_peek
|
|
173
173
|
source_code_uri: https://github.com/trix/pg_peek
|
|
174
|
+
changelog_uri: https://github.com/trix/pg_peek/releases
|
|
174
175
|
rubygems_mfa_required: 'true'
|
|
175
176
|
rdoc_options: []
|
|
176
177
|
require_paths:
|