pghero 3.7.0 → 4.0.0

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -2
  3. data/LICENSE.txt +1 -1
  4. data/README.md +4 -3
  5. data/app/assets/javascripts/pghero/Chart.bundle.js +2228 -5320
  6. data/app/assets/javascripts/pghero/application.js +104 -68
  7. data/app/assets/javascripts/pghero/highlight.min.js +330 -341
  8. data/app/assets/javascripts/pghero/nouislider.js +64 -5
  9. data/app/assets/stylesheets/pghero/application.css +7 -1
  10. data/app/controllers/pg_hero/home_controller.rb +75 -41
  11. data/app/views/layouts/pg_hero/application.html.erb +1 -1
  12. data/app/views/pg_hero/home/_live_queries_table.html.erb +4 -2
  13. data/app/views/pg_hero/home/_queries_table.html.erb +8 -7
  14. data/app/views/pg_hero/home/_query_stats_slider.html.erb +1 -0
  15. data/app/views/pg_hero/home/_suggested_index.html.erb +3 -3
  16. data/app/views/pg_hero/home/explain.html.erb +2 -1
  17. data/app/views/pg_hero/home/index.html.erb +17 -18
  18. data/app/views/pg_hero/home/index_bloat.html.erb +2 -2
  19. data/app/views/pg_hero/home/live_queries.html.erb +7 -3
  20. data/app/views/pg_hero/home/queries.html.erb +31 -6
  21. data/app/views/pg_hero/home/show_query.html.erb +5 -5
  22. data/app/views/pg_hero/home/space.html.erb +5 -4
  23. data/app/views/pg_hero/home/tune.html.erb +1 -1
  24. data/lib/generators/pghero/templates/config.yml.tt +6 -1
  25. data/lib/generators/pghero/templates/query_stats.rb.tt +7 -1
  26. data/lib/generators/pghero/templates/upgrade_query_stats.rb.tt +23 -0
  27. data/lib/generators/pghero/upgrade_query_stats_generator.rb +19 -0
  28. data/lib/pghero/database.rb +6 -9
  29. data/lib/pghero/engine.rb +3 -10
  30. data/lib/pghero/methods/basic.rb +28 -27
  31. data/lib/pghero/methods/connections.rb +16 -32
  32. data/lib/pghero/methods/explain.rb +13 -20
  33. data/lib/pghero/methods/indexes.rb +8 -5
  34. data/lib/pghero/methods/kill.rb +4 -1
  35. data/lib/pghero/methods/maintenance.rb +14 -20
  36. data/lib/pghero/methods/queries.rb +7 -5
  37. data/lib/pghero/methods/query_stats.rb +264 -238
  38. data/lib/pghero/methods/replication.rb +9 -20
  39. data/lib/pghero/methods/sequences.rb +1 -1
  40. data/lib/pghero/methods/settings.rb +6 -11
  41. data/lib/pghero/methods/space.rb +11 -8
  42. data/lib/pghero/methods/suggested_indexes.rb +15 -16
  43. data/lib/pghero/methods/system.rb +6 -106
  44. data/lib/pghero/methods/tables.rb +7 -3
  45. data/lib/pghero/query.rb +8 -0
  46. data/lib/pghero/query_stats.rb +3 -0
  47. data/lib/pghero/version.rb +1 -1
  48. data/lib/pghero.rb +10 -42
  49. data/licenses/LICENSE-chart.js.txt +1 -1
  50. metadata +8 -8
  51. data/app/assets/javascripts/pghero/jquery.js +0 -10993
  52. data/lib/pghero/methods/users.rb +0 -95
  53. data/licenses/LICENSE-jquery.txt +0 -20
@@ -1,39 +1,37 @@
1
- //= require ./jquery
2
1
  //= require ./nouislider
3
2
  //= require ./Chart.bundle
4
3
  //= require ./chartkick
5
4
  //= require ./highlight.min
6
5
 
7
6
  function highlightQueries() {
8
- $("pre code").each(function (i, block) {
9
- $(block).addClass("language-pgsql");
10
- hljs.highlightElement(block);
11
- });
7
+ for (const block of document.querySelectorAll("code.language-pgsql")) {
8
+ if (!block.dataset.highlighted) {
9
+ hljs.highlightElement(block);
10
+ }
11
+ }
12
12
  }
13
13
 
14
14
  function initSlider() {
15
15
  function roundTime(time) {
16
- var period = 1000 * 60 * 5;
16
+ const period = 1000 * 60 * 5;
17
17
  return new Date(Math.ceil(time.getTime() / period) * period);
18
18
  }
19
19
 
20
20
  function pad(num) {
21
- return (num < 10) ? "0" + num : num;
21
+ return num < 10 ? "0" + num : num;
22
22
  }
23
23
 
24
- var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
25
-
26
- var days = 1;
27
- var now = new Date();
28
- var sliderStartAt = roundTime(now) - days * 24 * 60 * 60 * 1000;
29
- var sliderMax = 24 * 12 * days;
24
+ const days = 1;
25
+ const now = new Date();
26
+ const sliderStartAt = roundTime(now) - days * 24 * 60 * 60 * 1000;
27
+ const sliderMax = 24 * 12 * days;
30
28
 
31
29
  startAt = startAt || sliderStartAt;
32
- var min = (startAt > 0) ? (startAt - sliderStartAt) / (1000 * 60 * 5) : 0;
30
+ const min = startAt > 0 ? (startAt - sliderStartAt) / (1000 * 60 * 5) : 0;
33
31
 
34
- var max = (endAt > 0) ? (endAt - sliderStartAt) / (1000 * 60 * 5) : sliderMax;
32
+ const max = endAt > 0 ? (endAt - sliderStartAt) / (1000 * 60 * 5) : sliderMax;
35
33
 
36
- var slider = document.getElementById("slider");
34
+ const slider = document.getElementById("slider");
37
35
 
38
36
  noUiSlider.create(slider, {
39
37
  range: {
@@ -46,35 +44,37 @@ function initSlider() {
46
44
  });
47
45
 
48
46
  // remove outline for mouse only
49
- $(".noUi-handle").mousedown(function () {
50
- $(this).addClass("no-outline");
51
- }).blur(function () {
52
- $(this).removeClass("no-outline");
47
+ const handle = document.querySelector(".noUi-handle");
48
+ handle.addEventListener("mousedown", function (e) {
49
+ e.target.classList.add("no-outline");
50
+ });
51
+ handle.addEventListener("blur", function (e) {
52
+ e.target.classList.remove("no-outline");
53
53
  });
54
54
 
55
55
  function updateText() {
56
- var values = slider.noUiSlider.get();
56
+ const values = slider.noUiSlider.get();
57
57
  setText("#range-start", values[0]);
58
58
  setText("#range-end", values[1]);
59
59
  }
60
60
 
61
61
  function setText(selector, offset) {
62
- var time = timeAt(offset);
62
+ const time = timeAt(offset);
63
63
 
64
- var html = "";
64
+ let html = "";
65
65
  if (time === now) {
66
66
  if (selector === "#range-end") {
67
67
  html = "Now";
68
68
  }
69
69
  } else {
70
- html = time.getDate() + " " + months[time.getMonth()] + " " + pad(time.getHours()) + ":" + pad(time.getMinutes());
70
+ html = time.toLocaleDateString(undefined, {month: 'short', day: 'numeric'}) + " " + pad(time.getHours()) + ":" + pad(time.getMinutes());
71
71
  }
72
- $(selector).text(html);
72
+ document.querySelector(selector).textContent = html;
73
73
  }
74
74
 
75
75
  function timeAt(offset) {
76
- var time = new Date(sliderStartAt + (offset * 5) * 60 * 1000);
77
- return (time > now) ? now : time;
76
+ const time = new Date(sliderStartAt + offset * 5 * 60 * 1000);
77
+ return time > now ? now : time;
78
78
  }
79
79
 
80
80
  function timeParam(time) {
@@ -82,19 +82,19 @@ function initSlider() {
82
82
  }
83
83
 
84
84
  function queriesPath(params) {
85
- var path = "queries";
86
- if (params.start_at || params.end_at || params.sort || params.min_average_time || params.min_calls || params.debug) {
87
- path += "?" + $.param(params);
85
+ let path = "queries";
86
+ if (params.start_at || params.end_at || params.sort || params.min_average_time || params.min_calls || params.user || params.debug) {
87
+ path += "?" + (new URLSearchParams(params)).toString();
88
88
  }
89
89
  return path;
90
90
  }
91
91
 
92
92
  function refreshStats(push) {
93
- var values = slider.noUiSlider.get();
94
- var startAt = push ? timeAt(values[0]) : new Date(window.startAt);
95
- var endAt = timeAt(values[1]);
93
+ const values = slider.noUiSlider.get();
94
+ const startAt = push ? timeAt(values[0]) : new Date(window.startAt);
95
+ const endAt = timeAt(values[1]);
96
96
 
97
- var params = {}
97
+ const params = {};
98
98
  if (startAt.getTime() != sliderStartAt) {
99
99
  params.start_at = timeParam(startAt);
100
100
  }
@@ -110,38 +110,44 @@ function initSlider() {
110
110
  if (minCalls) {
111
111
  params.min_calls = minCalls;
112
112
  }
113
+ if (user) {
114
+ params.user = user;
115
+ }
113
116
  if (debug) {
114
117
  params.debug = debug;
115
118
  }
116
119
 
117
- var path = queriesPath(params);
118
-
119
- $(".queries-table th a").each(function () {
120
- var p = $.extend({}, params, {sort: $(this).data("sort"), min_average_time: minAverageTime, min_calls: minCalls, debug: debug});
121
- if (!p.sort) {
122
- delete p.sort;
123
- }
124
- if (!p.min_average_time) {
125
- delete p.min_average_time;
126
- }
127
- if (!p.min_calls) {
128
- delete p.min_calls;
129
- }
130
- if (!p.debug) {
131
- delete p.debug;
132
- }
133
- $(this).attr("href", queriesPath(p));
134
- });
120
+ const showAll = document.getElementById("show-all");
121
+ if (showAll) {
122
+ const showAllParams = Object.assign({}, params);
123
+ delete showAllParams.user;
124
+ showAll.setAttribute("href", queriesPath(showAllParams));
125
+ }
135
126
 
127
+ for (const link of document.querySelectorAll(".queries-table th a")) {
128
+ const linkParams = Object.assign({}, params, {sort: link.getAttribute("data-sort")});
129
+ link.setAttribute("href", queriesPath(linkParams));
130
+ }
136
131
 
137
- var callback = function (response, status, xhr) {
138
- if (status === "error" ) {
139
- $(".queries-info").css("color", "red").text(xhr.status + " " + xhr.statusText);
140
- } else {
132
+ const queries = document.getElementById("queries");
133
+ queries.innerHTML = '<tr><td colspan="3"><p class="queries-info text-muted">...</p></td></tr>';
134
+ const path = queriesPath(params);
135
+ fetch(path, {headers: {"X-Requested-With": "XMLHttpRequest"}})
136
+ .then(function (response) {
137
+ if (!response.ok) {
138
+ throw new Error(response.statusText);
139
+ }
140
+ return response.text();
141
+ })
142
+ .then(function (text) {
143
+ queries.innerHTML = text;
141
144
  highlightQueries();
142
- }
143
- };
144
- $("#queries").html('<tr><td colspan="3"><p class="queries-info text-muted">...</p></td></tr>').load(path, callback);
145
+ })
146
+ .catch(function (error) {
147
+ const queriesInfo = document.querySelector(".queries-info");
148
+ queriesInfo.style.color = "red";
149
+ queriesInfo.textContent = error.message;
150
+ });
145
151
 
146
152
  if (push && history.pushState) {
147
153
  history.pushState(null, null, path);
@@ -153,21 +159,51 @@ function initSlider() {
153
159
  refreshStats(true);
154
160
  });
155
161
  updateText();
156
- $(function () {
162
+ document.addEventListener("DOMContentLoaded", function () {
157
163
  refreshStats(false);
158
164
  });
159
165
  }
160
166
 
161
- $(document).on("click", ".query-code", function () {
162
- this.style.maxHeight = "none";
167
+ document.addEventListener("click", function (e) {
168
+ const target = e.target.closest(".query-code");
169
+ if (target) {
170
+ target.style.maxHeight = "none";
171
+ }
172
+ });
173
+
174
+ document.addEventListener("click", function (e) {
175
+ const target = e.target.closest(".migration-link");
176
+ if (target) {
177
+ e.preventDefault();
178
+ target.parentElement.nextElementSibling.style.display = "block";
179
+ }
180
+ });
181
+
182
+ document.addEventListener("click", function (e) {
183
+ const target = e.target.closest(".show-details");
184
+ if (target) {
185
+ target.nextElementSibling.nextElementSibling.style.display = "block";
186
+ target.style.display = "none";
187
+ }
163
188
  });
164
189
 
165
- $(document).on("click", ".migration-link", function (e) {
166
- e.preventDefault();
167
- $(this).parent().next(".migration").css("display", "block");
190
+ document.addEventListener("click", function (e) {
191
+ const target = e.target.closest("#copy");
192
+ if (target) {
193
+ const explanation = document.getElementById("explanation").textContent;
194
+ navigator.clipboard.writeText(explanation).then(function () {
195
+ const buttonText = target.textContent;
196
+ target.textContent = "Copied!";
197
+ setTimeout(function () {
198
+ target.textContent = buttonText;
199
+ }, 2000);
200
+ });
201
+ }
168
202
  });
169
203
 
170
- $(document).on("click", ".show-details", function () {
171
- $(this).nextAll(".details").css("display", "block");
172
- $(this).css("display", "none");
204
+ document.addEventListener("DOMContentLoaded", function () {
205
+ const copyButton = document.getElementById("copy");
206
+ if (copyButton && navigator.clipboard) {
207
+ copyButton.classList.remove("hide");
208
+ }
173
209
  });