pghero 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pghero might be problematic. Click here for more details.

@@ -1,5 +1,5 @@
1
1
  /*
2
- *= require ./jquery.nouislider
2
+ *= require ./nouislider
3
3
  *= require ./arduino-light
4
4
  *= require_self
5
5
  */
@@ -0,0 +1,299 @@
1
+ /*! nouislider - 14.0.3 - 10/10/2019 */
2
+ /* Functional styling;
3
+ * These styles are required for noUiSlider to function.
4
+ * You don't need to change these rules to apply your design.
5
+ */
6
+ .noUi-target,
7
+ .noUi-target * {
8
+ -webkit-touch-callout: none;
9
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
10
+ -webkit-user-select: none;
11
+ -ms-touch-action: none;
12
+ touch-action: none;
13
+ -ms-user-select: none;
14
+ -moz-user-select: none;
15
+ user-select: none;
16
+ -moz-box-sizing: border-box;
17
+ box-sizing: border-box;
18
+ }
19
+ .noUi-target {
20
+ position: relative;
21
+ direction: ltr;
22
+ }
23
+ .noUi-base,
24
+ .noUi-connects {
25
+ width: 100%;
26
+ height: 100%;
27
+ position: relative;
28
+ z-index: 1;
29
+ }
30
+ /* Wrapper for all connect elements.
31
+ */
32
+ .noUi-connects {
33
+ overflow: hidden;
34
+ z-index: 0;
35
+ }
36
+ .noUi-connect,
37
+ .noUi-origin {
38
+ will-change: transform;
39
+ position: absolute;
40
+ z-index: 1;
41
+ top: 0;
42
+ left: 0;
43
+ -ms-transform-origin: 0 0;
44
+ -webkit-transform-origin: 0 0;
45
+ -webkit-transform-style: preserve-3d;
46
+ transform-origin: 0 0;
47
+ transform-style: flat;
48
+ }
49
+ .noUi-connect {
50
+ height: 100%;
51
+ width: 100%;
52
+ }
53
+ .noUi-origin {
54
+ height: 10%;
55
+ width: 10%;
56
+ }
57
+ /* Offset direction
58
+ */
59
+ html:not([dir="rtl"]) .noUi-horizontal .noUi-origin {
60
+ left: auto;
61
+ right: 0;
62
+ }
63
+ /* Give origins 0 height/width so they don't interfere with clicking the
64
+ * connect elements.
65
+ */
66
+ .noUi-vertical .noUi-origin {
67
+ width: 0;
68
+ }
69
+ .noUi-horizontal .noUi-origin {
70
+ height: 0;
71
+ }
72
+ .noUi-handle {
73
+ -webkit-backface-visibility: hidden;
74
+ backface-visibility: hidden;
75
+ position: absolute;
76
+ }
77
+ .noUi-touch-area {
78
+ height: 100%;
79
+ width: 100%;
80
+ }
81
+ .noUi-state-tap .noUi-connect,
82
+ .noUi-state-tap .noUi-origin {
83
+ -webkit-transition: transform 0.3s;
84
+ transition: transform 0.3s;
85
+ }
86
+ .noUi-state-drag * {
87
+ cursor: inherit !important;
88
+ }
89
+ /* Slider size and handle placement;
90
+ */
91
+ .noUi-horizontal {
92
+ height: 18px;
93
+ }
94
+ .noUi-horizontal .noUi-handle {
95
+ width: 34px;
96
+ height: 28px;
97
+ left: -17px;
98
+ top: -6px;
99
+ }
100
+ .noUi-vertical {
101
+ width: 18px;
102
+ }
103
+ .noUi-vertical .noUi-handle {
104
+ width: 28px;
105
+ height: 34px;
106
+ left: -6px;
107
+ top: -17px;
108
+ }
109
+ html:not([dir="rtl"]) .noUi-horizontal .noUi-handle {
110
+ right: -17px;
111
+ left: auto;
112
+ }
113
+ /* Styling;
114
+ * Giving the connect element a border radius causes issues with using transform: scale
115
+ */
116
+ .noUi-target {
117
+ background: #FAFAFA;
118
+ border-radius: 4px;
119
+ border: 1px solid #D3D3D3;
120
+ box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB;
121
+ }
122
+ .noUi-connects {
123
+ border-radius: 3px;
124
+ }
125
+ .noUi-connect {
126
+ background: #3FB8AF;
127
+ }
128
+ /* Handles and cursors;
129
+ */
130
+ .noUi-draggable {
131
+ cursor: ew-resize;
132
+ }
133
+ .noUi-vertical .noUi-draggable {
134
+ cursor: ns-resize;
135
+ }
136
+ .noUi-handle {
137
+ border: 1px solid #D9D9D9;
138
+ border-radius: 3px;
139
+ background: #FFF;
140
+ cursor: default;
141
+ box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #EBEBEB, 0 3px 6px -3px #BBB;
142
+ }
143
+ .noUi-active {
144
+ box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #DDD, 0 3px 6px -3px #BBB;
145
+ }
146
+ /* Handle stripes;
147
+ */
148
+ .noUi-handle:before,
149
+ .noUi-handle:after {
150
+ content: "";
151
+ display: block;
152
+ position: absolute;
153
+ height: 14px;
154
+ width: 1px;
155
+ background: #E8E7E6;
156
+ left: 14px;
157
+ top: 6px;
158
+ }
159
+ .noUi-handle:after {
160
+ left: 17px;
161
+ }
162
+ .noUi-vertical .noUi-handle:before,
163
+ .noUi-vertical .noUi-handle:after {
164
+ width: 14px;
165
+ height: 1px;
166
+ left: 6px;
167
+ top: 14px;
168
+ }
169
+ .noUi-vertical .noUi-handle:after {
170
+ top: 17px;
171
+ }
172
+ /* Disabled state;
173
+ */
174
+ [disabled] .noUi-connect {
175
+ background: #B8B8B8;
176
+ }
177
+ [disabled].noUi-target,
178
+ [disabled].noUi-handle,
179
+ [disabled] .noUi-handle {
180
+ cursor: not-allowed;
181
+ }
182
+ /* Base;
183
+ *
184
+ */
185
+ .noUi-pips,
186
+ .noUi-pips * {
187
+ -moz-box-sizing: border-box;
188
+ box-sizing: border-box;
189
+ }
190
+ .noUi-pips {
191
+ position: absolute;
192
+ color: #999;
193
+ }
194
+ /* Values;
195
+ *
196
+ */
197
+ .noUi-value {
198
+ position: absolute;
199
+ white-space: nowrap;
200
+ text-align: center;
201
+ }
202
+ .noUi-value-sub {
203
+ color: #ccc;
204
+ font-size: 10px;
205
+ }
206
+ /* Markings;
207
+ *
208
+ */
209
+ .noUi-marker {
210
+ position: absolute;
211
+ background: #CCC;
212
+ }
213
+ .noUi-marker-sub {
214
+ background: #AAA;
215
+ }
216
+ .noUi-marker-large {
217
+ background: #AAA;
218
+ }
219
+ /* Horizontal layout;
220
+ *
221
+ */
222
+ .noUi-pips-horizontal {
223
+ padding: 10px 0;
224
+ height: 80px;
225
+ top: 100%;
226
+ left: 0;
227
+ width: 100%;
228
+ }
229
+ .noUi-value-horizontal {
230
+ -webkit-transform: translate(-50%, 50%);
231
+ transform: translate(-50%, 50%);
232
+ }
233
+ .noUi-rtl .noUi-value-horizontal {
234
+ -webkit-transform: translate(50%, 50%);
235
+ transform: translate(50%, 50%);
236
+ }
237
+ .noUi-marker-horizontal.noUi-marker {
238
+ margin-left: -1px;
239
+ width: 2px;
240
+ height: 5px;
241
+ }
242
+ .noUi-marker-horizontal.noUi-marker-sub {
243
+ height: 10px;
244
+ }
245
+ .noUi-marker-horizontal.noUi-marker-large {
246
+ height: 15px;
247
+ }
248
+ /* Vertical layout;
249
+ *
250
+ */
251
+ .noUi-pips-vertical {
252
+ padding: 0 10px;
253
+ height: 100%;
254
+ top: 0;
255
+ left: 100%;
256
+ }
257
+ .noUi-value-vertical {
258
+ -webkit-transform: translate(0, -50%);
259
+ transform: translate(0, -50%);
260
+ padding-left: 25px;
261
+ }
262
+ .noUi-rtl .noUi-value-vertical {
263
+ -webkit-transform: translate(0, 50%);
264
+ transform: translate(0, 50%);
265
+ }
266
+ .noUi-marker-vertical.noUi-marker {
267
+ width: 5px;
268
+ height: 2px;
269
+ margin-top: -1px;
270
+ }
271
+ .noUi-marker-vertical.noUi-marker-sub {
272
+ width: 10px;
273
+ }
274
+ .noUi-marker-vertical.noUi-marker-large {
275
+ width: 15px;
276
+ }
277
+ .noUi-tooltip {
278
+ display: block;
279
+ position: absolute;
280
+ border: 1px solid #D9D9D9;
281
+ border-radius: 3px;
282
+ background: #fff;
283
+ color: #000;
284
+ padding: 5px;
285
+ text-align: center;
286
+ white-space: nowrap;
287
+ }
288
+ .noUi-horizontal .noUi-tooltip {
289
+ -webkit-transform: translate(-50%, 0);
290
+ transform: translate(-50%, 0);
291
+ left: 50%;
292
+ bottom: 120%;
293
+ }
294
+ .noUi-vertical .noUi-tooltip {
295
+ -webkit-transform: translate(0, -50%);
296
+ transform: translate(0, -50%);
297
+ top: 50%;
298
+ right: 120%;
299
+ }
@@ -4,20 +4,18 @@ module PgHero
4
4
 
5
5
  protect_from_forgery
6
6
 
7
- http_basic_authenticate_with name: ENV["PGHERO_USERNAME"], password: ENV["PGHERO_PASSWORD"] if ENV["PGHERO_PASSWORD"]
8
-
9
- if respond_to?(:before_action)
10
- before_action :check_api
11
- before_action :set_database
12
- before_action :set_query_stats_enabled
13
- before_action :set_show_details, only: [:index, :queries, :show_query]
14
- before_action :ensure_query_stats, only: [:queries]
15
- else
16
- # no need to check API in earlier versions
17
- before_filter :set_database
18
- before_filter :set_query_stats_enabled
19
- before_filter :set_show_details, only: [:index, :queries, :show_query]
20
- before_filter :ensure_query_stats, only: [:queries]
7
+ http_basic_authenticate_with name: PgHero.username, password: PgHero.password if PgHero.password
8
+
9
+ before_action :check_api
10
+ before_action :set_database
11
+ before_action :set_query_stats_enabled
12
+ before_action :set_show_details, only: [:index, :queries, :show_query]
13
+ before_action :ensure_query_stats, only: [:queries]
14
+
15
+ if PgHero.config["override_csp"]
16
+ after_action do
17
+ response.headers["Content-Security-Policy"] = "default-src 'self' 'unsafe-inline'"
18
+ end
21
19
  end
22
20
 
23
21
  def index
@@ -48,6 +46,7 @@ module PgHero
48
46
 
49
47
  @indexes = @database.indexes
50
48
  @invalid_indexes = @database.invalid_indexes(indexes: @indexes)
49
+ @invalid_constraints = @database.invalid_constraints
51
50
  @duplicate_indexes = @database.duplicate_indexes(indexes: @indexes)
52
51
 
53
52
  if @query_stats_enabled
@@ -100,7 +99,7 @@ module PgHero
100
99
  @relation = params[:relation]
101
100
  @title = @relation
102
101
  relation_space_stats = @database.relation_space_stats(@relation, schema: @schema)
103
- @chart_data = [{name: "Value", data: relation_space_stats.map { |r| [r[:captured_at], (r[:size_bytes].to_f / 1.megabyte).round(1)] }, library: chart_library_options}]
102
+ @chart_data = [{name: "Value", data: relation_space_stats.map { |r| [r[:captured_at].change(sec: 0), r[:size_bytes].to_i] }, library: chart_library_options}]
104
103
  end
105
104
 
106
105
  def index_bloat
@@ -230,7 +229,7 @@ module PgHero
230
229
 
231
230
  def free_space_stats
232
231
  render json: [
233
- {name: "Free Space", data: @database.free_space_stats(duration: 14.days, period: 1.hour).map { |k, v| [k, (v / 1.gigabyte).round] }, library: chart_library_options},
232
+ {name: "Free Space", data: @database.free_space_stats(duration: 14.days, period: 1.hour), library: chart_library_options},
234
233
  ]
235
234
  end
236
235
 
@@ -369,7 +368,7 @@ module PgHero
369
368
  end
370
369
 
371
370
  def chart_library_options
372
- {pointRadius: 0, pointHitRadius: 5, borderWidth: 4}
371
+ {pointRadius: 0, pointHoverRadius: 0, pointHitRadius: 5, borderWidth: 4}
373
372
  end
374
373
 
375
374
  def set_show_details
@@ -11,7 +11,17 @@
11
11
  <% queries.reverse.each do |query| %>
12
12
  <tr>
13
13
  <td><%= query[:pid] %></td>
14
- <td><%= number_with_delimiter(query[:duration_ms].round) %> ms</td>
14
+ <td>
15
+ <% sec = query[:duration_ms] / 1000.0 %>
16
+ <% if sec < 1.minute %>
17
+ <%= sec.round(1) %> s
18
+ <% elsif sec < 1.day %>
19
+ <%= Time.at(sec).utc.strftime("%H:%M:%S") %>
20
+ <% else %>
21
+ <% days = (sec / 1.day).floor %>
22
+ <%= days %>d <%= Time.at(sec - days.days).utc.strftime("%H:%M:%S") %>
23
+ <% end %>
24
+ </td>
15
25
  <td>
16
26
  <%= query[:state] %>
17
27
  <% if vacuum_progress[query[:pid]] %>
@@ -62,11 +62,17 @@
62
62
  (<%= link_to pluralize(@unreadable_sequences.size, "unreadable sequence", "unreadable sequences"), {unreadable: "t"} %>)
63
63
  <% end %>
64
64
  </div>
65
- <div class="alert alert-<%= @invalid_indexes.empty? ? "success" : "warning" %>">
66
- <% if @invalid_indexes.any? %>
67
- <%= pluralize(@invalid_indexes.size, "invalid index", "invalid indexes") %>
65
+ <div class="alert alert-<%= @invalid_indexes.empty? && @invalid_constraints.empty? ? "success" : "warning" %>">
66
+ <% if @invalid_indexes.empty? && @invalid_constraints.empty? %>
67
+ No invalid indexes or constraints
68
68
  <% else %>
69
- No invalid indexes
69
+ <% if @invalid_indexes.any? %>
70
+ <%= pluralize(@invalid_indexes.size, "invalid index", "invalid indexes") %>
71
+ <% end %>
72
+ <% if @invalid_constraints.any? %>
73
+ <% if @invalid_indexes.any? %>and<% end %>
74
+ <%= pluralize(@invalid_constraints.size, "invalid constraint", "invalid constraints") %>
75
+ <% end %>
70
76
  <% end %>
71
77
  </div>
72
78
  <% if @duplicate_indexes %>
@@ -332,6 +338,39 @@
332
338
  </div>
333
339
  <% end %>
334
340
 
341
+ <% if @invalid_constraints.any? %>
342
+ <div class="content">
343
+ <h1>Invalid Constraints</h1>
344
+
345
+ <p>These constraints are marked as <code>NOT VALID</code>. You should validate them.</p>
346
+
347
+ <table class="table">
348
+ <thead>
349
+ <tr>
350
+ <th>Name</th>
351
+ </tr>
352
+ </thead>
353
+ <tbody>
354
+ <% @invalid_constraints.each do |constraint| %>
355
+ <tr>
356
+ <td>
357
+ <%= constraint[:name] %>
358
+ <% if constraint[:schema] != "public" %>
359
+ <span class="text-muted"><%= constraint[:schema] %></span>
360
+ <% end %>
361
+ </td>
362
+ </tr>
363
+ <tr>
364
+ <td style="border-top: none; padding: 0;">
365
+ <pre><code>ALTER TABLE <%= pghero_pretty_ident(constraint[:table], schema: constraint[:schema]) %> VALIDATE CONSTRAINT <%= pghero_pretty_ident(constraint[:name]) %>;</code></pre>
366
+ </td>
367
+ </tr>
368
+ <% end %>
369
+ </tbody>
370
+ </table>
371
+ </div>
372
+ <% end %>
373
+
335
374
  <% if @duplicate_indexes && @duplicate_indexes.any? %>
336
375
  <div class="content">
337
376
  <h1>Duplicate Indexes</h1>
@@ -345,7 +384,7 @@
345
384
  </p>
346
385
 
347
386
  <div id="migration2" style="display: none;">
348
- <pre>rails g migration remove_unneeded_indexes</pre>
387
+ <pre>rails generate migration remove_unneeded_indexes</pre>
349
388
  <p>And paste</p>
350
389
  <pre style="overflow: scroll; white-space: pre; word-break: normal;"><% @duplicate_indexes.each do |query| %>
351
390
  remove_index <%= query[:unneeded_index][:table].to_sym.inspect %>, name: <%= query[:unneeded_index][:name].to_s.inspect %><% end %></pre>
@@ -387,12 +426,12 @@ remove_index <%= query[:unneeded_index][:table].to_sym.inspect %>, name: <%= que
387
426
  </p>
388
427
 
389
428
  <div id="migration3" style="display: none;">
390
- <pre>rails g migration add_suggested_indexes</pre>
429
+ <pre>rails generate migration add_suggested_indexes</pre>
391
430
  <p>And paste</p>
392
431
  <pre style="overflow: scroll; white-space: pre; word-break: normal;">commit_db_transaction
393
432
  <% @suggested_indexes.each do |index| %>
394
433
  <% if index[:using] && index[:using] != "btree" %>
395
- connection.execute("CREATE INDEX CONCURRENTLY ON <%= index[:table] %><% if index[:using] %> USING <%= index[:using] %><% end %> (<%= index[:columns].join(", ") %>)")
434
+ add_index <%= index[:table].to_sym.inspect %>, <%= index[:columns].first.inspect %>, using: <%= index[:using].inspect %>, algorithm: :concurrently
396
435
  <% else %>
397
436
  add_index <%= index[:table].to_sym.inspect %>, [<%= index[:columns].map(&:to_sym).map(&:inspect).join(", ") %>], algorithm: :concurrently<% end %>
398
437
  <% end %></pre>
@@ -449,7 +488,7 @@ pg_stat_statements.track = all</pre>
449
488
  </p>
450
489
 
451
490
  <div id="migration" style="display: none;">
452
- <pre>rails g migration remove_unused_indexes</pre>
491
+ <pre>rails generate migration remove_unused_indexes</pre>
453
492
  <p>And paste</p>
454
493
  <pre style="overflow: scroll; white-space: pre; word-break: normal;"><% @unused_indexes.each do |query| %>
455
494
  remove_index <%= query[:table].to_sym.inspect %>, name: <%= query[:index].to_s.inspect %><% end %></pre>