pg_peek 0.2.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52d233ab8cafc3dae1e3386c44b9244549344b3b4bf97bc9f053cfdb843bd223
4
- data.tar.gz: 3abf5a72e79b3ffff19323bb6fc7812c479bfd23c8bbee8667aff5a52732cb1f
3
+ metadata.gz: abc82aa27c9e7fcebbe27a38cc17ff102ae578afdd11d0be75908874ee5058e8
4
+ data.tar.gz: 7bc328de015861d67c0250ad0373c4639fe1a45c15426c084939d56409b74d5a
5
5
  SHA512:
6
- metadata.gz: 93640cabe15c0afecb89784c94623a77ccb118a1679a9fe821b58e4fb9f04f68de2e28b88222f475a5930f1daff722f577c65149159c498aece5b19e1fe20f9f
7
- data.tar.gz: 37074ace65e4e13d9953f4d342a8b4011a0fa7903b7141e29b8961d3db44ea680273c78b4835157605275f4578878b23d04e28df1287a99ce3505703b33b11ab
6
+ metadata.gz: 72349c589551ec7dd8b5875f1ccbd579a7bbeaba94588fd29251cec72f041f4610077963efdfdd47683f7741469f9ac0cc7f912453770698e5e68e346ea1a7a3
7
+ data.tar.gz: 8c336b095487533a4862016ee4ffd1d332f3dad19f2134a03716375df017809fa123b1cfa37c286a0d833efeffbc4104c126c2df5a50ae74dce5b7e9eb6ee5d3
@@ -1,4 +1,4 @@
1
- // pg_peek — no framework, no CDN, no import map. Three small behaviours.
1
+ // pg_peek — no framework, no CDN, no import map. A few small behaviours.
2
2
  (function () {
3
3
  "use strict";
4
4
 
@@ -112,6 +112,29 @@
112
112
  });
113
113
  }
114
114
 
115
+ // Long queries --------------------------------------------------------------
116
+ // Clamped to their first lines by CSS. Measured here, so only a query that
117
+ // is actually longer gets the toggle.
118
+ function initClamp() {
119
+ document.querySelectorAll("[data-clamp]").forEach(function (code) {
120
+ code.classList.add("clamped");
121
+ if (code.scrollHeight <= code.clientHeight) {
122
+ code.classList.remove("clamped");
123
+ return;
124
+ }
125
+
126
+ var button = document.createElement("button");
127
+ button.type = "button";
128
+ button.className = "clamp-toggle";
129
+ button.textContent = "show all";
130
+ button.addEventListener("click", function () {
131
+ var expanded = code.classList.toggle("expanded");
132
+ button.textContent = expanded ? "show less" : "show all";
133
+ });
134
+ code.insertAdjacentElement("afterend", button);
135
+ });
136
+ }
137
+
115
138
  // Database switcher -------------------------------------------------------
116
139
  function initSwitcher() {
117
140
  document.querySelectorAll("[data-switcher]").forEach(function (select) {
@@ -135,6 +158,7 @@
135
158
  initTheme();
136
159
  initFilter();
137
160
  initConfirm();
161
+ initClamp();
138
162
  initSwitcher();
139
163
  initReload();
140
164
  }
@@ -226,7 +226,30 @@ tbody th + td { text-align: right; }
226
226
  }
227
227
  .qualified .namespace > span { direction: ltr; unicode-bidi: embed; }
228
228
  .qualified .leaf { flex-shrink: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
229
- .sql { word-break: break-all; white-space: pre-wrap; background: none; border: 0; padding: 0; }
229
+ /* Formatted one clause per line (SqlFormat); a line longer than the cell
230
+ wraps at spaces and breaks inside a word only when nothing else fits. */
231
+ .sql { display: block; overflow-wrap: anywhere; white-space: pre-wrap; background: none; border: 0; padding: 0; }
232
+
233
+ /* A long query shows its first lines, with a toggle for the rest. The script
234
+ adds the class only where the query is longer, so short ones stay as they
235
+ are, and without it every query shows in full. */
236
+ .sql.clamped:not(.expanded) {
237
+ display: -webkit-box;
238
+ -webkit-box-orient: vertical;
239
+ -webkit-line-clamp: 3;
240
+ line-clamp: 3;
241
+ overflow: hidden;
242
+ }
243
+
244
+ button.clamp-toggle {
245
+ background: none;
246
+ border: 0;
247
+ padding: 0;
248
+ color: var(--dim);
249
+ font-size: inherit;
250
+ }
251
+
252
+ button.clamp-toggle:hover { color: var(--fg); }
230
253
 
231
254
  /* What issued a query, from its SQLcommenter tags: a link line under the SQL,
232
255
  plus every raw tag behind a disclosure. The page-level details/summary
@@ -21,7 +21,6 @@ class PgPeek::DatabasesController < PgPeek::ApplicationController
21
21
  def show
22
22
  @database = PgPeek::Database.find(params[:id]) or raise ActiveRecord::RecordNotFound, "Database not found"
23
23
  @pg_stat_statements = PgPeek::PgStatStatements.new(database: @database)
24
- @tables = PgPeek::Reports::Tables.new(database: @database)
25
24
  @vitals = @database.vitals
26
25
  @stats_reset_at = @pg_stat_statements.reset_at
27
26
  @sessions = PgPeek::Reports::Sessions.new(database: @database)
@@ -1,17 +1,28 @@
1
1
  # Manages the extension itself. What it records is read by QueriesController,
2
2
  # EndpointsController and JobsController.
3
3
  class PgPeek::PgStatStatementsController < PgPeek::ApplicationController
4
- def reset
5
- @database = PgPeek::Database.find(params[:database_id]) or raise ActiveRecord::RecordNotFound, "Database not found"
6
- pg_stat_statements = PgPeek::PgStatStatements.new(database: @database)
4
+ before_action :set_database
5
+
6
+ def show
7
+ return render "pg_peek/pg_stat_statements/not_preloaded" unless @pg_stat_statements.usable?
8
+
9
+ @connection_summary = @database.connection_summary
10
+ end
7
11
 
8
- if pg_stat_statements.resettable?
9
- pg_stat_statements.reset!
12
+ def reset
13
+ if @pg_stat_statements.resettable?
14
+ @pg_stat_statements.reset!
10
15
  flash[:notice] = "pg_stat_statements has been reset."
11
16
  else
12
17
  flash[:alert] = "This role may not reset pg_stat_statements; the statistics were left as they are."
13
18
  end
14
19
 
15
- redirect_to database_path(@database, anchor: "pg_stat_statements")
20
+ redirect_to database_pg_stat_statements_path(@database)
16
21
  end
22
+
23
+ private
24
+ def set_database
25
+ @database = PgPeek::Database.find(params[:database_id]) or raise ActiveRecord::RecordNotFound, "Database not found"
26
+ @pg_stat_statements = PgPeek::PgStatStatements.new(database: @database)
27
+ end
17
28
  end
@@ -1,6 +1,16 @@
1
1
  class PgPeek::TablesController < PgPeek::ApplicationController
2
+ before_action :set_database
3
+
4
+ def index
5
+ @tables = PgPeek::Reports::Tables.new(database: @database)
6
+ end
7
+
2
8
  def show
3
- @database = PgPeek::Database.find(params[:database_id]) or raise ActiveRecord::RecordNotFound, "Database not found"
4
9
  @table = @database.find_table(params[:name]) or raise ActiveRecord::RecordNotFound, "Table '#{params[:name]}' not found"
5
10
  end
11
+
12
+ private
13
+ def set_database
14
+ @database = PgPeek::Database.find(params[:database_id]) or raise ActiveRecord::RecordNotFound, "Database not found"
15
+ end
6
16
  end
@@ -102,14 +102,15 @@ module PgPeek::ApplicationHelper
102
102
  end
103
103
 
104
104
  # Every pg_stat_statements figure is cumulative since the last reset. Each
105
- # page says so in its header and links to the overview block that resets it.
105
+ # page says so in its header and links to the extension's page, which
106
+ # resets it.
106
107
  # Within the first hour the figures cover too little to go by, and the link
107
108
  # is marked provisional.
108
109
  def stats_since_link(database, reset_at)
109
110
  provisional = reset_at && reset_at > 1.hour.ago
110
111
 
111
112
  link_to "stats since #{format_time_ago(reset_at)}",
112
- database_path(database, anchor: "pg_stat_statements"),
113
+ database_pg_stat_statements_path(database),
113
114
  class: ("provisional" if provisional),
114
115
  title: provisional ? "figures may not be representative yet" : "figures are cumulative since the last pg_stat_statements reset"
115
116
  end
@@ -50,7 +50,7 @@ module PgPeek::ReportHelper
50
50
  # link into the page that already aggregates this exact query across every
51
51
  # call site) and every raw tag behind a disclosure, for the rest.
52
52
  def sql_cell(report, sql)
53
- code = tag.code(strip_sqlcommenter(sql).squish, class: "sql")
53
+ code = tag.code(PgPeek::SqlFormat.format(strip_sqlcommenter(sql)), class: "sql", data: { clamp: true })
54
54
  tags = PgPeek::SqlComment.tags(sql)
55
55
  return code if tags.empty?
56
56
 
@@ -93,6 +93,37 @@ class PgPeek::PgStatStatements
93
93
  Time.zone.parse(value.to_s) if value.present?
94
94
  end
95
95
 
96
+ # How many statements the server dropped since the last reset because the
97
+ # table was full (pg_stat_statements.max). Above zero, every total is an
98
+ # undercount: whatever was dropped took its calls and time with it.
99
+ def dealloc
100
+ return unless usable?
101
+
102
+ connection.select_value(PgPeek::QueryLoader.mark(<<~SQL)).to_i
103
+ SELECT dealloc FROM pg_stat_statements_info
104
+ SQL
105
+ end
106
+
107
+ # The table is shared by every database on the server, so max is measured
108
+ # against all of them, not just this one.
109
+ def server_statement_count
110
+ return unless usable?
111
+
112
+ connection.select_value(PgPeek::QueryLoader.mark(<<~SQL)).to_i
113
+ SELECT count(*) FROM pg_stat_statements
114
+ SQL
115
+ end
116
+
117
+ # pg_stat_statements.* settings, by name without the prefix.
118
+ def settings
119
+ return {} unless usable?
120
+
121
+ connection.select_rows(PgPeek::QueryLoader.mark(<<~SQL)).to_h
122
+ SELECT substr(name, length('pg_stat_statements.') + 1), setting
123
+ FROM pg_settings WHERE name LIKE 'pg_stat_statements.%' ORDER BY name
124
+ SQL
125
+ end
126
+
96
127
  def outliers
97
128
  return unless usable?
98
129
 
@@ -0,0 +1,74 @@
1
+ # Lays a query out one clause per line, so the first lines of a long query say
2
+ # what it reads and from where, and its joins and conditions read as a list.
3
+ # Only line breaks and spacing change. Breaks go in at the top level only --
4
+ # never inside parentheses, quotes or a CASE -- so subqueries and function
5
+ # calls stay on one line, and a query this does not understand comes out with
6
+ # an odd break at worst.
7
+ class PgPeek::SqlFormat
8
+ TOKEN = /'(?:[^']|'')*'|"(?:[^"]|"")*"|[(),;]|[^\s'"(),;]+|\s+/
9
+ CLAUSES = %w[SELECT FROM WHERE GROUP HAVING WINDOW ORDER LIMIT OFFSET FETCH FOR
10
+ UNION INTERSECT EXCEPT RETURNING SET VALUES].freeze
11
+ JOIN_PREFIXES = %w[INNER LEFT RIGHT FULL CROSS NATURAL].freeze
12
+ CONDITIONS = %w[WHERE HAVING].freeze
13
+
14
+ def self.format(sql)
15
+ tokens = []
16
+ space = false
17
+ sql.to_s.strip.scan(TOKEN) do |token|
18
+ if token.match?(/\A\s/)
19
+ space = true
20
+ else
21
+ tokens << [ token, space ]
22
+ space = false
23
+ end
24
+ end
25
+
26
+ out = +""
27
+ depth = case_depth = 0
28
+ clause = nil
29
+ between = false
30
+
31
+ tokens.each_with_index do |(token, space_before), index|
32
+ word = token.upcase
33
+ top = index.positive? && depth.zero? && case_depth.zero?
34
+ previous = tokens[index - 1]&.first&.upcase if index.positive?
35
+ following = tokens[index + 1]&.first&.upcase
36
+
37
+ line_break = nil
38
+ if top
39
+ if CLAUSES.include?(word)
40
+ line_break = "\n"
41
+ clause = word
42
+ elsif JOIN_PREFIXES.include?(word) && %w[JOIN OUTER].include?(following)
43
+ line_break = "\n"
44
+ clause = "JOIN"
45
+ elsif word == "JOIN" && !(JOIN_PREFIXES + %w[OUTER]).include?(previous)
46
+ line_break = "\n"
47
+ clause = "JOIN"
48
+ elsif word == "BETWEEN"
49
+ between = true
50
+ elsif word == "AND" && between
51
+ between = false
52
+ elsif %w[AND OR].include?(word) && CONDITIONS.include?(clause)
53
+ line_break = "\n "
54
+ end
55
+ end
56
+
57
+ if line_break
58
+ out << line_break
59
+ elsif space_before && !out.empty?
60
+ out << " "
61
+ end
62
+ out << token
63
+
64
+ case word
65
+ when "(" then depth += 1
66
+ when ")" then depth -= 1 if depth.positive?
67
+ when "CASE" then case_depth += 1
68
+ when "END" then case_depth -= 1 if case_depth.positive?
69
+ end
70
+ end
71
+
72
+ out
73
+ end
74
+ end
@@ -26,6 +26,7 @@
26
26
  <li><%= link_to "queries", database_queries_path(@database) %></li>
27
27
  <li><%= link_to "endpoints", database_endpoints_path(@database) %></li>
28
28
  <li><%= link_to "jobs", database_jobs_path(@database) %></li>
29
+ <li><%= link_to "tables", database_tables_path(@database) %></li>
29
30
  <li><%= link_to "indexes", database_indexes_path(@database) %></li>
30
31
  <li><%= link_to "activity", database_activity_path(@database) %></li>
31
32
  <li class="current-database">
@@ -107,64 +107,8 @@ end</code></pre>
107
107
  limit: PgPeek::DatabasesController::OVERVIEW_ROWS %>
108
108
  <% end %>
109
109
  </section>
110
-
111
- <section class="wide" id="tables" data-filter>
112
- <h2>tables (<%= @tables.rows.size %>)</h2>
113
- <input type="search"
114
- placeholder="filter tables… (press / to focus)"
115
- data-filter-input
116
- data-filter-noun="table"
117
- autocomplete="off">
118
- <p class="dim" data-filter-count></p>
119
-
120
- <%= render "pg_peek/reports/report",
121
- report: @tables,
122
- link_column: :name,
123
- link: ->(row) { database_table_path(@database, row["name"]) } %>
124
-
125
- <p class="dim" data-filter-empty hidden>No tables match that filter.</p>
126
- </section>
127
110
  </div>
128
111
 
129
- <% if @pg_stat_statements.usable? %>
130
- <section id="pg_stat_statements">
131
- <h2>pg_stat_statements <%= @pg_stat_statements.installed_version %></h2>
132
- <p class="dim">
133
- Queries, endpoints and jobs are cumulative since
134
- <%= @stats_reset_at.strftime("%Y-%m-%d %H:%M:%S %Z") %> (<%= format_time_ago(@stats_reset_at) %>),
135
- <%= pluralize(number_with_delimiter(@pg_stat_statements.statement_count), "statement") %> tracked.
136
- A reset starts a fresh window: exact counts and attribution, at the
137
- price of the history so far.
138
- <% peers = @database.server_peers %>
139
- <% if peers.any? %>
140
- It also clears <%= peers.map(&:name).to_sentence %>, which
141
- <%= peers.one? ? "shares" : "share" %> this server: PostgreSQL keeps
142
- one reset time for all of them.
143
- <% end %>
144
- </p>
145
- <%= button_to "reset statistics",
146
- reset_database_pg_stat_statements_path(@database),
147
- method: :delete,
148
- class: "danger",
149
- disabled: !@pg_stat_statements.resettable?,
150
- form_class: "inline",
151
- form: { data: { confirm: "Reset pg_stat_statements? This clears statement statistics for every database on this server." } } %>
152
- <% unless @pg_stat_statements.resettable? %>
153
- <p class="dim">
154
- <%= @connection_summary["username"] %> may not reset the statistics: the extension
155
- allows it only to superusers and roles granted it. To allow it:
156
- </p>
157
- <pre><code>GRANT EXECUTE ON FUNCTION pg_stat_statements_reset TO <%= @connection_summary["username"] %>;</code></pre>
158
- <% end %>
159
- <% if @pg_stat_statements.outdated? %>
160
- <p class="dim">
161
- Version <%= @pg_stat_statements.default_version %> is available:
162
- <code>ALTER EXTENSION pg_stat_statements UPDATE;</code>
163
- </p>
164
- <% end %>
165
- </section>
166
- <% end %>
167
-
168
112
  <details>
169
113
  <summary>database details</summary>
170
114
  <pre>name: <%= @database.name %>
@@ -1,5 +1,6 @@
1
1
  <div class="toolbar">
2
2
  <h1><code class="name"><%= @endpoint %></code></h1>
3
+ <small class="dim"><%= stats_since_link(@database, @pg_stat_statements.reset_at) %></small>
3
4
  <%= link_to "← endpoints", database_endpoints_path(@database) %>
4
5
  </div>
5
6
 
@@ -24,13 +24,6 @@
24
24
  collecting, so this page is working &mdash; there is simply nothing in it
25
25
  yet. Jobs appear as they execute their first database query.
26
26
  </p>
27
- <% if @stats_reset_at %>
28
- <p>
29
- Statistics were last reset <strong><%= format_time_ago(@stats_reset_at) %></strong>
30
- (<%= @stats_reset_at %>), so anything that ran before then is gone.
31
- Restarting PostgreSQL clears them too.
32
- </p>
33
- <% end %>
34
27
  </article>
35
28
  <% end %>
36
29
  <% else %>
@@ -1,5 +1,6 @@
1
1
  <div class="toolbar">
2
2
  <h1><code class="name"><%= @job_class %></code></h1>
3
+ <small class="dim"><%= stats_since_link(@database, @pg_stat_statements.reset_at) %></small>
3
4
  <%= link_to "← jobs", database_jobs_path(@database) %>
4
5
  </div>
5
6
 
@@ -0,0 +1,87 @@
1
+ <% reset_at = @pg_stat_statements.reset_at %>
2
+ <% settings = @pg_stat_statements.settings %>
3
+ <% dealloc = @pg_stat_statements.dealloc %>
4
+
5
+ <div class="toolbar">
6
+ <h1>pg_stat_statements <small class="dim"><%= @pg_stat_statements.installed_version %></small></h1>
7
+ </div>
8
+
9
+ <section>
10
+ <h2>collection</h2>
11
+ <table>
12
+ <tbody>
13
+ <tr>
14
+ <th>stats since</th>
15
+ <td><%= reset_at.strftime("%Y-%m-%d %H:%M:%S %Z") %> (<%= format_time_ago(reset_at) %>)</td>
16
+ </tr>
17
+ <tr>
18
+ <th>statements tracked</th>
19
+ <td>
20
+ <%= number_with_delimiter(@pg_stat_statements.server_statement_count) %>
21
+ <% if settings["max"] %>of <%= number_with_delimiter(settings["max"].to_i) %><% end %>
22
+ <span class="dim">(this database: <%= number_with_delimiter(@pg_stat_statements.statement_count) %>)</span>
23
+ </td>
24
+ </tr>
25
+ <tr>
26
+ <th>dropped when full</th>
27
+ <td><%= number_with_delimiter(dealloc) %></td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
31
+ <% if dealloc.positive? %>
32
+ <p class="dim">
33
+ The server dropped statements to make room since the last reset, so every
34
+ total here undercounts. Raise <code>pg_stat_statements.max</code>
35
+ (needs a server restart) or reset more often.
36
+ </p>
37
+ <% end %>
38
+ </section>
39
+
40
+ <section>
41
+ <h2>settings</h2>
42
+ <table>
43
+ <tbody>
44
+ <% settings.each do |name, value| %>
45
+ <tr>
46
+ <th><code>pg_stat_statements.<%= name %></code></th>
47
+ <td><%= value %></td>
48
+ </tr>
49
+ <% end %>
50
+ </tbody>
51
+ </table>
52
+ <% if @pg_stat_statements.outdated? %>
53
+ <p class="dim">
54
+ Version <%= @pg_stat_statements.default_version %> is available:
55
+ <code>ALTER EXTENSION pg_stat_statements UPDATE;</code>
56
+ </p>
57
+ <% end %>
58
+ </section>
59
+
60
+ <section id="reset">
61
+ <h2>reset</h2>
62
+ <p class="dim">
63
+ Queries, endpoints and jobs are cumulative since the last reset. A reset
64
+ starts a fresh window: exact counts and attribution, at the price of the
65
+ history so far. Restarting PostgreSQL clears them too.
66
+ <% peers = @database.server_peers %>
67
+ <% if peers.any? %>
68
+ It also clears <%= peers.map(&:name).to_sentence %>, which
69
+ <%= peers.one? ? "shares" : "share" %> this server: PostgreSQL keeps
70
+ one reset time for all of them.
71
+ <% end %>
72
+ </p>
73
+ <%= button_to "reset statistics",
74
+ reset_database_pg_stat_statements_path(@database),
75
+ method: :delete,
76
+ class: "danger",
77
+ disabled: !@pg_stat_statements.resettable?,
78
+ form_class: "inline",
79
+ form: { data: { confirm: "Reset pg_stat_statements? This clears statement statistics for every database on this server." } } %>
80
+ <% unless @pg_stat_statements.resettable? %>
81
+ <p class="dim">
82
+ <%= @connection_summary["username"] %> may not reset the statistics: the extension
83
+ allows it only to superusers and roles granted it. To allow it:
84
+ </p>
85
+ <pre><code>GRANT EXECUTE ON FUNCTION pg_stat_statements_reset TO <%= @connection_summary["username"] %>;</code></pre>
86
+ <% end %>
87
+ </section>
@@ -0,0 +1,18 @@
1
+ <div class="toolbar">
2
+ <h1>tables</h1>
3
+ </div>
4
+
5
+ <input type="search"
6
+ placeholder="filter tables… (press / to focus)"
7
+ data-filter-input
8
+ data-filter-noun="table"
9
+ autofocus
10
+ autocomplete="off">
11
+ <p class="dim" data-filter-count></p>
12
+
13
+ <%= render "pg_peek/reports/report",
14
+ report: @tables,
15
+ link_column: :name,
16
+ link: ->(row) { database_table_path(@database, row["name"]) } %>
17
+
18
+ <p class="dim" data-filter-empty hidden>No tables match that filter.</p>
@@ -1,6 +1,6 @@
1
1
  <div class="toolbar">
2
2
  <h1><code class="name"><%= @table.name %></code></h1>
3
- <%= link_to "← tables", database_path(@database, anchor: "tables") %>
3
+ <%= link_to "← tables", database_tables_path(@database) %>
4
4
  </div>
5
5
 
6
6
  <% seconds_elapsed = @table.seconds_since_reset %>
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  PgPeek::Engine.routes.draw do
2
2
  resources :databases, only: [ :index, :show ] do
3
3
  resources :queries, only: [ :index ]
4
- resources :tables, only: [ :show ], param: :name
4
+ resources :tables, only: [ :index, :show ], param: :name
5
5
  resources :endpoints, only: [ :index ]
6
6
  # admin/posts#index lives at endpoints/admin/posts/index: the slashes of a
7
7
  # namespace stay as they are, and no # needs encoding.
@@ -10,7 +10,7 @@ PgPeek::Engine.routes.draw do
10
10
  resource :activity, only: [ :show ], controller: "activity"
11
11
  resources :jobs, only: [ :index, :show ], param: :job_class
12
12
 
13
- resource :pg_stat_statements, only: [] do
13
+ resource :pg_stat_statements, only: [ :show ] do
14
14
  delete :reset
15
15
  end
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module PgPeek
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
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.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomasz Mazur
@@ -69,6 +69,7 @@ files:
69
69
  - app/models/pg_peek/database.rb
70
70
  - app/models/pg_peek/pg_stat_statements.rb
71
71
  - app/models/pg_peek/sql_comment.rb
72
+ - app/models/pg_peek/sql_format.rb
72
73
  - app/models/pg_peek/table.rb
73
74
  - app/queries/pg_peek/pg14/activity/sessions.sql
74
75
  - app/queries/pg_peek/pg14/activity/summary.sql
@@ -159,8 +160,10 @@ files:
159
160
  - app/views/pg_peek/jobs/show.html.erb
160
161
  - app/views/pg_peek/pg_stat_statements/_preload_instructions.html.erb
161
162
  - app/views/pg_peek/pg_stat_statements/not_preloaded.html.erb
163
+ - app/views/pg_peek/pg_stat_statements/show.html.erb
162
164
  - app/views/pg_peek/queries/index.html.erb
163
165
  - app/views/pg_peek/reports/_report.html.erb
166
+ - app/views/pg_peek/tables/index.html.erb
164
167
  - app/views/pg_peek/tables/show.html.erb
165
168
  - config/routes.rb
166
169
  - lib/generators/pg_peek/install/install_generator.rb
@@ -194,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
197
  - !ruby/object:Gem::Version
195
198
  version: '0'
196
199
  requirements: []
197
- rubygems_version: 3.6.9
200
+ rubygems_version: 4.0.21
198
201
  specification_version: 4
199
202
  summary: PostgreSQL query performance monitoring for Rails applications
200
203
  test_files: []