pghero 2.6.0 → 2.7.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7334e516c44c91605f37b8fd22b57a46b147cfe4191796e48624e8a15b45ca0
4
- data.tar.gz: 1235c0626b5a7f2061f2bcfce062d4716f3de7a5c198aeed489eaf9d0c7549c0
3
+ metadata.gz: 38e408389854af6f5a2a71ee70bc3d66c257a299b1a1fec255c279819db00482
4
+ data.tar.gz: 2f492cf50fd99032ec9ac9cfd03df70ef5c4d45ea55a0c14a5c608102b5aa106
5
5
  SHA512:
6
- metadata.gz: 52944fa0d91437a0fd42dc4e8705a4f833a3d60937748f1cbffd03aa9471af8ce2e30db7645c419590696dcbbe0e700941b838216ae3da7a5bc211f0067188a5
7
- data.tar.gz: 8ff19581f7245ebc804ef49af131c9f6eca5ed8110b878ccb8e1b15cf94c2951d88e4329e1565145e03b21c63e3798be400dc74b2655c7489dca75ba9ca4aec5
6
+ metadata.gz: edc72b96fb3c766f41682c22aa2287a99c610435687794d0a09395821a129709b0e464f7e9031d43cac7a6d1bc334cb419885dc32b87d9d4e58a75d810c3ba30
7
+ data.tar.gz: a4ad92136153cb3f607bddb0b289d3c180a6a653f7a6eac9b47ca6fe7fa04626923f7b84e80951577c9a94314c2487ac7dccb4cdd5f2483a3076473893bac13e
@@ -1,3 +1,8 @@
1
+ ## 2.7.0 (2020-08-04)
2
+
3
+ - Fixed CSRF vulnerability with non-session based authentication
4
+ - Added `database`, `user`, and `query_hash` options to `reset_query_stats` method
5
+
1
6
  ## 2.6.0 (2020-07-09)
2
7
 
3
8
  - Added support for Postgres 13 beta 2
@@ -2,7 +2,7 @@ module PgHero
2
2
  class HomeController < ActionController::Base
3
3
  layout "pg_hero/application"
4
4
 
5
- protect_from_forgery
5
+ protect_from_forgery with: :exception
6
6
 
7
7
  http_basic_authenticate_with name: PgHero.username, password: PgHero.password if PgHero.password
8
8
 
@@ -196,13 +196,13 @@ module PgHero
196
196
  # stats for old databases are not cleaned up since we can't use an index
197
197
  def clean_query_stats
198
198
  each_database do |database|
199
- PgHero::QueryStats.where(database: database.id).where("captured_at < ?", 14.days.ago).delete_all
199
+ database.clean_query_stats
200
200
  end
201
201
  end
202
202
 
203
203
  def clean_space_stats
204
204
  each_database do |database|
205
- PgHero::SpaceStats.where(database: database.id).where("captured_at < ?", 90.days.ago).delete_all
205
+ database.clean_space_stats
206
206
  end
207
207
  end
208
208
 
@@ -18,6 +18,10 @@ module PgHero
18
18
  select_one("SELECT current_database()")
19
19
  end
20
20
 
21
+ def current_user
22
+ select_one("SELECT current_user")
23
+ end
24
+
21
25
  def server_version
22
26
  @server_version ||= select_one("SHOW server_version")
23
27
  end
@@ -56,8 +56,46 @@ module PgHero
56
56
  true
57
57
  end
58
58
 
59
- def reset_query_stats(raise_errors: false)
60
- execute("SELECT pg_stat_statements_reset()")
59
+ # TODO scope by database in PgHero 3.0
60
+ # (add database: database_name to options)
61
+ def reset_query_stats(**options)
62
+ reset_instance_query_stats(**options)
63
+ end
64
+
65
+ # resets query stats for the entire instance
66
+ # it's possible to reset stats for a specific
67
+ # database, user or query hash in Postgres 12+
68
+ def reset_instance_query_stats(database: nil, user: nil, query_hash: nil, raise_errors: false)
69
+ if database || user || query_hash
70
+ raise PgHero::Error, "Requires PostgreSQL 12+" if server_version_num < 120000
71
+
72
+ if database
73
+ database_id = execute("SELECT oid FROM pg_database WHERE datname = #{quote(database)}").first.try(:[], "oid")
74
+ raise PgHero::Error, "Database not found: #{database}" unless database_id
75
+ else
76
+ database_id = 0
77
+ end
78
+
79
+ if user
80
+ user_id = execute("SELECT usesysid FROM pg_user WHERE usename = #{quote(user)}").first.try(:[], "usesysid")
81
+ raise PgHero::Error, "User not found: #{user}" unless user_id
82
+ else
83
+ user_id = 0
84
+ end
85
+
86
+ if query_hash
87
+ query_id = query_hash.to_i
88
+ # may not be needed
89
+ # but not intuitive that all query hashes are reset with 0
90
+ raise PgHero::Error, "Invalid query hash: #{query_hash}" if query_id == 0
91
+ else
92
+ query_id = 0
93
+ end
94
+
95
+ execute("SELECT pg_stat_statements_reset(#{quote(user_id.to_i)}, #{quote(database_id.to_i)}, #{quote(query_id.to_i)})")
96
+ else
97
+ execute("SELECT pg_stat_statements_reset()")
98
+ end
61
99
  true
62
100
  rescue ActiveRecord::StatementInvalid => e
63
101
  raise e if raise_errors
@@ -104,31 +142,32 @@ module PgHero
104
142
  query_stats[database_id] = query_stats(limit: 1000000, database: database_name)
105
143
  end
106
144
 
107
- supports_query_hash = supports_query_hash?
145
+ query_stats = query_stats.select { |_, v| v.any? }
108
146
 
109
- if query_stats.any? { |_, v| v.any? } && reset_query_stats(raise_errors: raise_errors)
147
+ # nothing to do
148
+ return if query_stats.empty?
149
+
150
+ # use mapping, not query stats here
151
+ # TODO add option for this, and make default in PgHero 3.0
152
+ if false # mapping.size == 1 && server_version_num >= 120000
110
153
  query_stats.each do |db_id, db_query_stats|
111
- if db_query_stats.any?
112
- values =
113
- db_query_stats.map do |qs|
114
- [
115
- db_id,
116
- qs[:query],
117
- qs[:total_minutes] * 60 * 1000,
118
- qs[:calls],
119
- now,
120
- supports_query_hash ? qs[:query_hash] : nil,
121
- qs[:user]
122
- ]
123
- end
124
-
125
- columns = %w[database query total_time calls captured_at query_hash user]
126
- insert_stats("pghero_query_stats", columns, values)
154
+ if reset_query_stats(database: mapping[db_id], raise_errors: raise_errors)
155
+ insert_query_stats(db_id, db_query_stats, now)
156
+ end
157
+ end
158
+ else
159
+ if reset_query_stats(raise_errors: raise_errors)
160
+ query_stats.each do |db_id, db_query_stats|
161
+ insert_query_stats(db_id, db_query_stats, now)
127
162
  end
128
163
  end
129
164
  end
130
165
  end
131
166
 
167
+ def clean_query_stats
168
+ PgHero::QueryStats.where(database: id).where("captured_at < ?", 14.days.ago).delete_all
169
+ end
170
+
132
171
  def slow_queries(query_stats: nil, **options)
133
172
  query_stats ||= self.query_stats(options)
134
173
  query_stats.select { |q| q[:calls].to_i >= slow_query_calls.to_i && q[:average_time].to_f >= slow_query_ms.to_f }
@@ -287,6 +326,24 @@ module PgHero
287
326
  def normalize_query(query)
288
327
  squish(query.to_s.gsub(/\?(, ?\?)+/, "?").gsub(/\/\*.+?\*\//, ""))
289
328
  end
329
+
330
+ def insert_query_stats(db_id, db_query_stats, now)
331
+ values =
332
+ db_query_stats.map do |qs|
333
+ [
334
+ db_id,
335
+ qs[:query],
336
+ qs[:total_minutes] * 60 * 1000,
337
+ qs[:calls],
338
+ now,
339
+ supports_query_hash? ? qs[:query_hash] : nil,
340
+ qs[:user]
341
+ ]
342
+ end
343
+
344
+ columns = %w[database query total_time calls captured_at query_hash user]
345
+ insert_stats("pghero_query_stats", columns, values)
346
+ end
290
347
  end
291
348
  end
292
349
  end
@@ -129,6 +129,10 @@ module PgHero
129
129
  insert_stats("pghero_space_stats", columns, values) if values.any?
130
130
  end
131
131
 
132
+ def clean_space_stats
133
+ PgHero::SpaceStats.where(database: id).where("captured_at < ?", 90.days.ago).delete_all
134
+ end
135
+
132
136
  def space_stats_enabled?
133
137
  table_exists?("pghero_space_stats")
134
138
  end
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "2.6.0"
2
+ VERSION = "2.7.0"
3
3
  end
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.6.0
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-09 00:00:00.000000000 Z
11
+ date: 2020-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord