pghero 2.7.1 → 2.8.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/app/controllers/pg_hero/home_controller.rb +3 -6
- data/app/helpers/pg_hero/home_helper.rb +10 -0
- data/app/views/pg_hero/home/index.html.erb +3 -0
- data/app/views/pg_hero/home/maintenance.html.erb +2 -2
- data/lib/pghero.rb +14 -2
- data/lib/pghero/database.rb +5 -2
- data/lib/pghero/methods/basic.rb +1 -1
- data/lib/pghero/methods/queries.rb +2 -1
- data/lib/pghero/methods/sequences.rb +2 -1
- data/lib/pghero/methods/suggested_indexes.rb +117 -28
- data/lib/pghero/methods/system.rb +1 -2
- data/lib/pghero/methods/users.rb +2 -2
- data/lib/pghero/version.rb +1 -1
- metadata +7 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b4a596a016b5c33a99013818181ef967c26dcec092ee787b6629eac091ad121
|
4
|
+
data.tar.gz: 63fc2a949a62828a4537e54fb087c5a5b143bc88ab2753b4d777d4f793fb34ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: beda0eb3c89c9a3928b9d998e7e9ae8858adda77873c9dc0d03836498a765c219815c6441b814d3b23da78f02f759d71737eb51236135cfb72c6372358043c37
|
7
|
+
data.tar.gz: 020de38d5b406ede495f44ad16ce6a5bde8ebb59c49027af4dc49afb88cf768bc20f3c064fe72070f2739eb2979d2b95d0f7cdbdd106851252e6c81f646d3e82
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
## 2.8.1 (2021-03-25)
|
2
|
+
|
3
|
+
- Added support for pg_query 2
|
4
|
+
|
5
|
+
## 2.8.0 (2021-03-14)
|
6
|
+
|
7
|
+
- No longer show walsender in long running queries
|
8
|
+
- Show relative time on maintenance page
|
9
|
+
- Added support for `PGHERO_TZ` for Docker and Linux
|
10
|
+
|
11
|
+
## 2.7.4 (2021-02-01)
|
12
|
+
|
13
|
+
- Fixed error on redirect with Ruby 3
|
14
|
+
- Fixed error with unparsable sequences
|
15
|
+
|
16
|
+
## 2.7.3 (2020-11-23)
|
17
|
+
|
18
|
+
- Improved index suggestions when hash, GiST, GIN, or BRIN index present
|
19
|
+
|
20
|
+
## 2.7.2 (2020-09-10)
|
21
|
+
|
22
|
+
- Fixed error with historical query stats
|
23
|
+
|
1
24
|
## 2.7.1 (2020-09-07)
|
2
25
|
|
3
26
|
- Added `/health` endpoint to Docker image and Linux packages
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -4,11 +4,11 @@ A performance dashboard for Postgres
|
|
4
4
|
|
5
5
|
[See it in action](https://pghero.dokkuapp.com/)
|
6
6
|
|
7
|
-
[![Screenshot](https://pghero.dokkuapp.com/assets/pghero-
|
7
|
+
[![Screenshot](https://pghero.dokkuapp.com/assets/pghero-ebb9c8e11434fd0e4ead81db88fe255b29cbbc845ec315b79e6c5e25015bc921.png)](https://pghero.dokkuapp.com/)
|
8
8
|
|
9
9
|
:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
|
10
10
|
|
11
|
-
[![Build Status](https://
|
11
|
+
[![Build Status](https://github.com/ankane/pghero/workflows/build/badge.svg?branch=master)](https://github.com/ankane/pghero/actions) [![Docker Pulls](https://img.shields.io/docker/pulls/ankane/pghero)](https://hub.docker.com/repository/docker/ankane/pghero)
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -34,7 +34,8 @@ module PgHero
|
|
34
34
|
@inactive_replication_slots = @database.replication_slots.select { |r| !r[:active] }
|
35
35
|
end
|
36
36
|
|
37
|
-
@
|
37
|
+
@walsender_queries, long_running_queries = @database.long_running_queries.partition { |q| q[:backend_type] == "walsender" }
|
38
|
+
@autovacuum_queries, @long_running_queries = long_running_queries.partition { |q| q[:query].starts_with?("autovacuum:") }
|
38
39
|
|
39
40
|
connection_states = @database.connection_states
|
40
41
|
@total_connections = connection_states.values.sum
|
@@ -373,11 +374,7 @@ module PgHero
|
|
373
374
|
protected
|
374
375
|
|
375
376
|
def redirect_backward(options = {})
|
376
|
-
|
377
|
-
redirect_back options.merge(fallback_location: root_path)
|
378
|
-
else
|
379
|
-
redirect_to :back, options
|
380
|
-
end
|
377
|
+
redirect_back fallback_location: root_path, **options
|
381
378
|
end
|
382
379
|
|
383
380
|
def set_database
|
@@ -26,5 +26,15 @@ module PgHero
|
|
26
26
|
ret << ", column: #{columns.inspect}" if columns
|
27
27
|
ret
|
28
28
|
end
|
29
|
+
|
30
|
+
def pghero_formatted_vacuum_times(time)
|
31
|
+
content_tag(:span, title: pghero_formatted_date_time(time)) do
|
32
|
+
"#{time_ago_in_words(time, include_seconds: true).sub(/(over|about|almost) /, "").sub("less than", "<")} ago"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pghero_formatted_date_time(time)
|
37
|
+
l time.in_time_zone(@time_zone), format: :long
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
@@ -26,6 +26,9 @@
|
|
26
26
|
<% if @autovacuum_queries.any? %>
|
27
27
|
<span class="tiny"><%= @autovacuum_queries.size %> autovacuum</span>
|
28
28
|
<% end %>
|
29
|
+
<% if @walsender_queries.any? %>
|
30
|
+
<span class="tiny"><%= @walsender_queries.size %> walsender</span>
|
31
|
+
<% end %>
|
29
32
|
</div>
|
30
33
|
<% if @extended %>
|
31
34
|
<div class="alert alert-<%= @good_cache_rate ? "success" : "warning" %>">
|
@@ -24,7 +24,7 @@
|
|
24
24
|
<td>
|
25
25
|
<% time = [table[:last_autovacuum], table[:last_vacuum]].compact.max %>
|
26
26
|
<% if time %>
|
27
|
-
<%=
|
27
|
+
<%= pghero_formatted_vacuum_times(time) %>
|
28
28
|
<% else %>
|
29
29
|
<span class="text-muted">Unknown</span>
|
30
30
|
<% end %>
|
@@ -32,7 +32,7 @@
|
|
32
32
|
<td>
|
33
33
|
<% time = [table[:last_autoanalyze], table[:last_analyze]].compact.max %>
|
34
34
|
<% if time %>
|
35
|
-
<%=
|
35
|
+
<%= pghero_formatted_vacuum_times(time) %>
|
36
36
|
<% else %>
|
37
37
|
<span class="text-muted">Unknown</span>
|
38
38
|
<% end %>
|
data/lib/pghero.rb
CHANGED
@@ -113,13 +113,13 @@ module PgHero
|
|
113
113
|
|
114
114
|
if !ENV["PGHERO_DATABASE_URL"] && spec_supported?
|
115
115
|
ActiveRecord::Base.configurations.configs_for(env_name: env, include_replicas: true).each do |db|
|
116
|
-
databases[db.
|
116
|
+
databases[db.send(spec_name_key)] = {"spec" => db.send(spec_name_key)}
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
120
|
if databases.empty?
|
121
121
|
databases["primary"] = {
|
122
|
-
"url" => ENV["PGHERO_DATABASE_URL"] || ActiveRecord::Base
|
122
|
+
"url" => ENV["PGHERO_DATABASE_URL"] || connection_config(ActiveRecord::Base)
|
123
123
|
}
|
124
124
|
end
|
125
125
|
|
@@ -211,6 +211,18 @@ module PgHero
|
|
211
211
|
ActiveRecord::VERSION::MAJOR >= 6
|
212
212
|
end
|
213
213
|
|
214
|
+
# private
|
215
|
+
def connection_config(model)
|
216
|
+
ActiveRecord::VERSION::STRING.to_f >= 6.1 ? model.connection_db_config.configuration_hash : model.connection_config
|
217
|
+
end
|
218
|
+
|
219
|
+
# private
|
220
|
+
# Rails 6.1 deprecate `spec_name` and use `name` for configurations
|
221
|
+
# https://github.com/rails/rails/pull/38536
|
222
|
+
def spec_name_key
|
223
|
+
ActiveRecord::VERSION::STRING.to_f >= 6.1 ? :name : :spec_name
|
224
|
+
end
|
225
|
+
|
214
226
|
private
|
215
227
|
|
216
228
|
def each_database
|
data/lib/pghero/database.rb
CHANGED
@@ -149,11 +149,14 @@ module PgHero
|
|
149
149
|
# resolve spec
|
150
150
|
if !url && config["spec"]
|
151
151
|
raise Error, "Spec requires Rails 6+" unless PgHero.spec_supported?
|
152
|
-
|
152
|
+
config_options = {env_name: PgHero.env, PgHero.spec_name_key => config["spec"], include_replicas: true}
|
153
|
+
resolved = ActiveRecord::Base.configurations.configs_for(**config_options)
|
153
154
|
raise Error, "Spec not found: #{config["spec"]}" unless resolved
|
154
|
-
url = resolved.config
|
155
|
+
url = ActiveRecord::VERSION::STRING.to_f >= 6.1 ? resolved.configuration_hash : resolved.config
|
155
156
|
end
|
156
157
|
|
158
|
+
url = url.dup
|
159
|
+
|
157
160
|
Class.new(PgHero::Connection) do
|
158
161
|
def self.name
|
159
162
|
"PgHero::Connection::Database#{object_id}"
|
data/lib/pghero/methods/basic.rb
CHANGED
@@ -42,7 +42,7 @@ module PgHero
|
|
42
42
|
retries = 0
|
43
43
|
begin
|
44
44
|
result = conn.select_all(add_source(squish(sql)))
|
45
|
-
if ActiveRecord::VERSION::
|
45
|
+
if ActiveRecord::VERSION::STRING.to_f >= 6.1
|
46
46
|
result = result.map(&:symbolize_keys)
|
47
47
|
else
|
48
48
|
result = result.map { |row| Hash[row.map { |col, val| [col.to_sym, result.column_types[col].send(:cast_value, val)] }] }
|
@@ -12,7 +12,8 @@ module PgHero
|
|
12
12
|
query,
|
13
13
|
COALESCE(query_start, xact_start) AS started_at,
|
14
14
|
EXTRACT(EPOCH FROM NOW() - COALESCE(query_start, xact_start)) * 1000.0 AS duration_ms,
|
15
|
-
usename AS user
|
15
|
+
usename AS user,
|
16
|
+
#{server_version_num >= 100000 ? "backend_type" : "NULL AS backend_type"}
|
16
17
|
FROM
|
17
18
|
pg_stat_activity
|
18
19
|
WHERE
|
@@ -30,7 +30,23 @@ module PgHero
|
|
30
30
|
if best_index[:found]
|
31
31
|
index = best_index[:index]
|
32
32
|
best_index[:table_indexes] = indexes_by_table[index[:table]].to_a
|
33
|
-
|
33
|
+
|
34
|
+
# indexes of same type
|
35
|
+
indexes = existing_columns[index[:using] || "btree"][index[:table]]
|
36
|
+
|
37
|
+
if best_index[:structure][:sort].empty?
|
38
|
+
# gist indexes without an opclass
|
39
|
+
# (opclass is part of column name, so columns won't match if opclass present)
|
40
|
+
indexes += existing_columns["gist"][index[:table]]
|
41
|
+
|
42
|
+
# hash indexes work for equality
|
43
|
+
indexes += existing_columns["hash"][index[:table]] if best_index[:structure][:where].all? { |v| v[:op] == "=" }
|
44
|
+
|
45
|
+
# brin indexes work for all
|
46
|
+
indexes += existing_columns["brin"][index[:table]]
|
47
|
+
end
|
48
|
+
|
49
|
+
covering_index = indexes.find { |e| index_covers?(e.map { |v| v.sub(/ inet_ops\z/, "") }, index[:columns]) }
|
34
50
|
if covering_index
|
35
51
|
best_index[:covering_index] = covering_index
|
36
52
|
best_index[:explanation] = "Covered by index on (#{covering_index.join(", ")})"
|
@@ -86,7 +102,7 @@ module PgHero
|
|
86
102
|
# get stats about columns for relevant tables
|
87
103
|
tables = parts.values.map { |t| t[:table] }.uniq
|
88
104
|
# TODO get schema from query structure, then try search path
|
89
|
-
schema =
|
105
|
+
schema = PgHero.connection_config(connection_model)[:schema] || "public"
|
90
106
|
if tables.any?
|
91
107
|
row_stats = Hash[table_stats(table: tables, schema: schema).map { |i| [i[:table], i[:estimated_rows]] }]
|
92
108
|
col_stats = column_stats(table: tables, schema: schema).group_by { |i| i[:table] }
|
@@ -185,36 +201,69 @@ module PgHero
|
|
185
201
|
rescue PgQuery::ParseError
|
186
202
|
return {error: "Parse error"}
|
187
203
|
end
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
204
|
+
|
205
|
+
if PgQuery::VERSION.to_i >= 2
|
206
|
+
return {error: "Unknown structure"} unless tree.stmts.size == 1
|
207
|
+
|
208
|
+
tree = tree.stmts.first.stmt
|
209
|
+
|
210
|
+
table = parse_table_v2(tree) rescue nil
|
211
|
+
unless table
|
212
|
+
error =
|
213
|
+
case tree.node
|
214
|
+
when :insert_stmt
|
215
|
+
"INSERT statement"
|
216
|
+
when :variable_set_stmt
|
217
|
+
"SET statement"
|
218
|
+
when :select_stmt
|
219
|
+
if (tree.select_stmt.from_clause.first.join_expr rescue false)
|
220
|
+
"JOIN not supported yet"
|
221
|
+
end
|
206
222
|
end
|
207
|
-
|
208
|
-
|
209
|
-
end
|
223
|
+
return {error: error || "Unknown structure"}
|
224
|
+
end
|
210
225
|
|
211
|
-
|
212
|
-
|
213
|
-
|
226
|
+
select = tree[tree.node.to_s]
|
227
|
+
where = (select.where_clause ? parse_where_v2(select.where_clause) : []) rescue nil
|
228
|
+
return {error: "Unknown structure"} unless where
|
214
229
|
|
215
|
-
|
230
|
+
sort = (select.sort_clause ? parse_sort_v2(select.sort_clause) : []) rescue []
|
216
231
|
|
217
|
-
|
232
|
+
{table: table, where: where, sort: sort}
|
233
|
+
else
|
234
|
+
# TODO remove support for pg_query < 2 in PgHero 3.0
|
235
|
+
|
236
|
+
return {error: "Unknown structure"} unless tree.size == 1
|
237
|
+
|
238
|
+
tree = tree.first
|
239
|
+
|
240
|
+
# pg_query 1.0.0
|
241
|
+
tree = tree["RawStmt"]["stmt"] if tree["RawStmt"]
|
242
|
+
|
243
|
+
table = parse_table(tree) rescue nil
|
244
|
+
unless table
|
245
|
+
error =
|
246
|
+
case tree.keys.first
|
247
|
+
when "InsertStmt"
|
248
|
+
"INSERT statement"
|
249
|
+
when "VariableSetStmt"
|
250
|
+
"SET statement"
|
251
|
+
when "SelectStmt"
|
252
|
+
if (tree["SelectStmt"]["fromClause"].first["JoinExpr"] rescue false)
|
253
|
+
"JOIN not supported yet"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
return {error: error || "Unknown structure"}
|
257
|
+
end
|
258
|
+
|
259
|
+
select = tree.values.first
|
260
|
+
where = (select["whereClause"] ? parse_where(select["whereClause"]) : []) rescue nil
|
261
|
+
return {error: "Unknown structure"} unless where
|
262
|
+
|
263
|
+
sort = (select["sortClause"] ? parse_sort(select["sortClause"]) : []) rescue []
|
264
|
+
|
265
|
+
{table: table, where: where, sort: sort}
|
266
|
+
end
|
218
267
|
end
|
219
268
|
|
220
269
|
# TODO better row estimation
|
@@ -251,6 +300,17 @@ module PgHero
|
|
251
300
|
end
|
252
301
|
end
|
253
302
|
|
303
|
+
def parse_table_v2(tree)
|
304
|
+
case tree.node
|
305
|
+
when :select_stmt
|
306
|
+
tree.select_stmt.from_clause.first.range_var.relname
|
307
|
+
when :delete_stmt
|
308
|
+
tree.delete_stmt.relation.relname
|
309
|
+
when :update_stmt
|
310
|
+
tree.update_stmt.relation.relname
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
254
314
|
def parse_table(tree)
|
255
315
|
case tree.keys.first
|
256
316
|
when "SelectStmt"
|
@@ -262,6 +322,26 @@ module PgHero
|
|
262
322
|
end
|
263
323
|
end
|
264
324
|
|
325
|
+
# TODO capture values
|
326
|
+
def parse_where_v2(tree)
|
327
|
+
aexpr = tree.a_expr
|
328
|
+
|
329
|
+
if tree.bool_expr
|
330
|
+
if tree.bool_expr.boolop == :AND_EXPR
|
331
|
+
tree.bool_expr.args.flat_map { |v| parse_where_v2(v) }
|
332
|
+
else
|
333
|
+
raise "Not Implemented"
|
334
|
+
end
|
335
|
+
elsif aexpr && ["=", "<>", ">", ">=", "<", "<=", "~~", "~~*", "BETWEEN"].include?(aexpr.name.first.string.str)
|
336
|
+
[{column: aexpr.lexpr.column_ref.fields.last.string.str, op: aexpr.name.first.string.str}]
|
337
|
+
elsif tree.null_test
|
338
|
+
op = tree.null_test.nulltesttype == :IS_NOT_NULL ? "not_null" : "null"
|
339
|
+
[{column: tree.null_test.arg.column_ref.fields.last.string.str, op: op}]
|
340
|
+
else
|
341
|
+
raise "Not Implemented"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
265
345
|
# TODO capture values
|
266
346
|
def parse_where(tree)
|
267
347
|
aexpr = tree["A_Expr"]
|
@@ -282,6 +362,15 @@ module PgHero
|
|
282
362
|
end
|
283
363
|
end
|
284
364
|
|
365
|
+
def parse_sort_v2(sort_clause)
|
366
|
+
sort_clause.map do |v|
|
367
|
+
{
|
368
|
+
column: v.sort_by.node.column_ref.fields.last.string.str,
|
369
|
+
direction: v.sort_by.sortby_dir == :SORTBY_DESC ? "desc" : "asc"
|
370
|
+
}
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
285
374
|
def parse_sort(sort_clause)
|
286
375
|
sort_clause.map do |v|
|
287
376
|
{
|
@@ -144,7 +144,7 @@ module PgHero
|
|
144
144
|
|
145
145
|
# validate input since we need to interpolate below
|
146
146
|
raise Error, "Invalid metric name" unless metric_name =~ /\A[a-z\/_]+\z/i
|
147
|
-
raise Error, "Invalid database id" unless gcp_database_id =~ /\A[a-
|
147
|
+
raise Error, "Invalid database id" unless gcp_database_id =~ /\A[a-z0-9\-:]+\z/i
|
148
148
|
|
149
149
|
# we handle three situations:
|
150
150
|
# 1. google-cloud-monitoring-v3
|
@@ -276,7 +276,6 @@ module PgHero
|
|
276
276
|
|
277
277
|
def add_missing_data(data, start_time, end_time, period)
|
278
278
|
time = start_time
|
279
|
-
end_time = end_time
|
280
279
|
while time < end_time
|
281
280
|
data[time] ||= nil
|
282
281
|
time += period
|
data/lib/pghero/methods/users.rb
CHANGED
@@ -5,7 +5,7 @@ module PgHero
|
|
5
5
|
# TODO quote in 3.0, but still not officially supported
|
6
6
|
def create_user(user, password: nil, schema: "public", database: nil, readonly: false, tables: nil)
|
7
7
|
password ||= random_password
|
8
|
-
database ||=
|
8
|
+
database ||= PgHero.connection_config(connection_model)[:database]
|
9
9
|
|
10
10
|
commands =
|
11
11
|
[
|
@@ -44,7 +44,7 @@ module PgHero
|
|
44
44
|
# documented as unsafe to pass user input
|
45
45
|
# TODO quote in 3.0, but still not officially supported
|
46
46
|
def drop_user(user, schema: "public", database: nil)
|
47
|
-
database ||=
|
47
|
+
database ||= PgHero.connection_config(connection_model)[:database]
|
48
48
|
|
49
49
|
# thanks shiftb
|
50
50
|
commands =
|
data/lib/pghero/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pghero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,91 +24,7 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5'
|
27
|
-
|
28
|
-
name: activerecord-import
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bundler
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: minitest
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rake
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: pg
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: pg_query
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
description:
|
27
|
+
description:
|
112
28
|
email: andrew@chartkick.com
|
113
29
|
executables: []
|
114
30
|
extensions: []
|
@@ -191,7 +107,7 @@ homepage: https://github.com/ankane/pghero
|
|
191
107
|
licenses:
|
192
108
|
- MIT
|
193
109
|
metadata: {}
|
194
|
-
post_install_message:
|
110
|
+
post_install_message:
|
195
111
|
rdoc_options: []
|
196
112
|
require_paths:
|
197
113
|
- lib
|
@@ -206,8 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
122
|
- !ruby/object:Gem::Version
|
207
123
|
version: '0'
|
208
124
|
requirements: []
|
209
|
-
rubygems_version: 3.
|
210
|
-
signing_key:
|
125
|
+
rubygems_version: 3.2.3
|
126
|
+
signing_key:
|
211
127
|
specification_version: 4
|
212
128
|
summary: A performance dashboard for Postgres
|
213
129
|
test_files: []
|