rails-pg-extras 5.5.0 → 5.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a02d3f90a286c80e215584b471293e8f2aaaaf33a56c56ca42e938d763b08dd
4
- data.tar.gz: 6326377418830e39a9105347a61e65837e64c1c80e390faa661a98468d6ad565
3
+ metadata.gz: 12c092356cb3e67c70abd5e49d61227fda3872353846b2fc18f23e83e5b33526
4
+ data.tar.gz: 795c689588341078ed71d1ab6e522ba3b2d26e968d5f1a060983f70c0a163732
5
5
  SHA512:
6
- metadata.gz: 4f89bd2d2b48f6cc82d9b0663e8d8a02f317d6aa4cc1052f0b123a126abf4bb5286068f1844b82a78c49b5b3fc835331fe16c7488ebc7ecd3b00bbdfeefcfe62
7
- data.tar.gz: 33fdf43584ee3b879c2497ff28fe90f6494bbdadb77732692805cd99c25ec569d8ce94ff0839a4a255d92c36a5015df2b2f623cc04a8bd1d86dacd4306a45a09
6
+ metadata.gz: 821ad0700c0b8d6fc352e15f7c45591354ac49d061c16d67e55b1778ae1ea4b979bfd62f76dc0b0bebd6cf8716803fb7e799434deba397d28496ac634a4c34af
7
+ data.tar.gz: 5104d36a6a7e7a709f9e8be494178624b2e727d89e6958729dd00497245f11d2f8ae9a329c2ae848a00d41bf85e97b0eadb8cf17597678249e0150aa5837c120
@@ -12,7 +12,7 @@ jobs:
12
12
  strategy:
13
13
  fail-fast: false
14
14
  matrix:
15
- ruby-version: ['3.3', '3.2', '3.1', '3.0', '2.7', '2.6']
15
+ ruby-version: ['3.3', '3.2', '3.1', '3.0', '2.7']
16
16
  steps:
17
17
  - uses: actions/checkout@v4
18
18
  - name: Run PostgreSQL 12
data/README.md CHANGED
@@ -135,7 +135,7 @@ You can enable UI using a Rails engine by adding the following code in `config/r
135
135
  mount RailsPgExtras::Web::Engine, at: 'pg_extras'
136
136
  ```
137
137
 
138
- You can enable HTTP basic auth by specifying `Rails.application.credentials.pg_extras.user` (or `RAILS_PG_EXTRAS_USER`) and `Rails.application.credentials.pg_extras.user` (or `RAILS_PG_EXTRAS_PASSWORD`) values. Authentication is mandatory unless you specify `RAILS_PG_EXTRAS_PUBLIC_DASHBOARD=true` or set `RailsPgExtras.configuration.public_dashboard = true`.
138
+ You can enable HTTP basic auth by specifying `Rails.application.credentials.pg_extras.user` (or `RAILS_PG_EXTRAS_USER`) and `Rails.application.credentials.pg_extras.password` (or `RAILS_PG_EXTRAS_PASSWORD`) values. Authentication is mandatory unless you specify `RAILS_PG_EXTRAS_PUBLIC_DASHBOARD=true` or set `RailsPgExtras.configuration.public_dashboard = true`.
139
139
 
140
140
  You can configure available web actions in `config/initializers/rails_pg_extras.rb`:
141
141
 
@@ -210,6 +210,47 @@ you can add this info to the output:
210
210
 
211
211
  ![Marginalia logs](https://github.com/pawurb/rails-pg-extras/raw/main/marginalia-logs.png)
212
212
 
213
+ ### `missing_fk_indexes`
214
+
215
+ This method lists columns likely to be foreign keys (i.e. column name ending in `_id` and related table exists) but don't have an index. It's recommended to always index foreign key columns because they are used for searching relation objects.
216
+
217
+ You can add indexes on the columns returned by this query and later check if they are receiving scans using the [unused_indexes method](#unused_indexes). Please remember that each index decreases write performance and autovacuuming overhead, so be careful when adding multiple indexes to often updated tables.
218
+
219
+ ```ruby
220
+ RailsPgExtras.missing_fk_indexes(args: { table_name: "users" })
221
+
222
+ +---------------------------------+
223
+ | Missing foreign key indexes |
224
+ +-------------------+-------------+
225
+ | table | column_name |
226
+ +-------------------+-------------+
227
+ | feedbacks | team_id |
228
+ | votes | user_id |
229
+ +-------------------+-------------+
230
+
231
+ ```
232
+
233
+ `table_name` argument is optional, if omitted, the method will display missing fk indexes for all the tables.
234
+
235
+ ## `missing_fk_constraints`
236
+
237
+ Similarly to the previous method, this one shows columns likely to be foreign keys that don't have a corresponding foreign key constraint. Foreign key constraints improve data integrity in the database by preventing relations with nonexisting objects. You can read more about the benefits of using foreign keys [in this blog post](https://pawelurbanek.com/rails-postgresql-data-integrity).
238
+
239
+ ```ruby
240
+ RailsPgExtras.missing_fk_constraints(args: { table_name: "users" })
241
+
242
+ +---------------------------------+
243
+ | Missing foreign key constraints |
244
+ +-------------------+-------------+
245
+ | table | column_name |
246
+ +-------------------+-------------+
247
+ | feedbacks | team_id |
248
+ | votes | user_id |
249
+ +-------------------+-------------+
250
+
251
+ ```
252
+
253
+ `table_name` argument is optional, if omitted, method will display missing fk constraints for all the tables.
213
254
 
214
255
  ### `table_info`
215
256
 
@@ -3,6 +3,14 @@ require "rails_pg_extras/version"
3
3
 
4
4
  module RailsPgExtras::Web
5
5
  class ApplicationController < ActionController::Base
6
+ def self.get_user
7
+ Rails.application.try(:credentials).try(:pg_extras).try(:user) || ENV["RAILS_PG_EXTRAS_USER"]
8
+ end
9
+
10
+ def self.get_password
11
+ Rails.application.try(:credentials).try(:pg_extras).try(:password) || ENV["RAILS_PG_EXTRAS_PASSWORD"]
12
+ end
13
+
6
14
  before_action :validate_credentials!
7
15
  layout "rails_pg_extras/web/application"
8
16
 
@@ -14,27 +22,14 @@ module RailsPgExtras::Web
14
22
 
15
23
  ACTIONS = %i[kill_all pg_stat_statements_reset add_extensions]
16
24
 
17
- user = get_user
18
- password = get_password
19
-
20
- if user.present? && password.present?
21
- http_basic_authenticate_with name: user, password: password
25
+ if get_user.present? && get_password.present?
26
+ http_basic_authenticate_with name: get_user, password: get_password
22
27
  end
23
28
 
24
29
  def validate_credentials!
25
- if (get_user.blank? || get_password.blank?) && RailsPgExtras.configuration.public_dashboard != true
30
+ if (self.class.get_user.blank? || self.class.get_password.blank?) && RailsPgExtras.configuration.public_dashboard != true
26
31
  raise "Missing credentials for rails-pg-extras dashboard! If you want to enable public dashboard please set RAILS_PG_EXTRAS_PUBLIC_DASHBOARD=true"
27
32
  end
28
33
  end
29
-
30
- private
31
-
32
- def get_user
33
- Rails.application.try(:credentials).try(:pg_extras).try(:user) || ENV["RAILS_PG_EXTRAS_USER"]
34
- end
35
-
36
- def get_password
37
- Rails.application.try(:credentials).try(:pg_extras).try(:password) || ENV["RAILS_PG_EXTRAS_PASSWORD"]
38
- end
39
34
  end
40
35
  end
@@ -20,8 +20,10 @@ module RailsPgExtras::Web
20
20
 
21
21
  private
22
22
 
23
+ SKIP_QUERIES = %i[table_schema table_foreign_keys].freeze
24
+
23
25
  def load_queries
24
- @all_queries = (RailsPgExtras::QUERIES - RailsPgExtras::Web::ACTIONS).inject({}) do |memo, query_name|
26
+ @all_queries = (RailsPgExtras::QUERIES - RailsPgExtras::Web::ACTIONS - SKIP_QUERIES).inject({}) do |memo, query_name|
25
27
  unless query_name.in? %i[mandelbrot]
26
28
  memo[query_name] = { disabled: query_disabled?(query_name) }
27
29
  end
@@ -6,6 +6,8 @@ require "rails_pg_extras/diagnose_data"
6
6
  require "rails_pg_extras/diagnose_print"
7
7
  require "rails_pg_extras/index_info"
8
8
  require "rails_pg_extras/index_info_print"
9
+ require "rails_pg_extras/missing_fk_indexes"
10
+ require "rails_pg_extras/missing_fk_constraints"
9
11
  require "rails_pg_extras/table_info"
10
12
  require "rails_pg_extras/table_info_print"
11
13
 
@@ -26,32 +28,12 @@ module RailsPgExtras
26
28
  end
27
29
 
28
30
  def self.run_query(query_name:, in_format:, args: {})
29
- if %i(calls outliers).include?(query_name)
30
- pg_stat_statements_version_sql = "SELECT installed_version
31
- FROM pg_available_extensions
32
- WHERE name = 'pg_stat_statements'"
33
- if (version = RailsPgExtras.connection.execute(pg_stat_statements_version_sql)
34
- .to_a[0].fetch("installed_version", nil))
35
- if Gem::Version.new(version) < Gem::Version.new(NEW_PG_STAT_STATEMENTS)
36
- query_name = "#{query_name}_legacy".to_sym
37
- elsif Gem::Version.new(version) >= Gem::Version.new(PG_STAT_STATEMENTS_17)
38
- query_name = "#{query_name}_17".to_sym
39
- end
40
- end
41
- end
42
-
43
- sql = if (custom_args = DEFAULT_ARGS[query_name].merge(args)) != {}
44
- RubyPgExtras.sql_for(query_name: query_name) % custom_args
45
- else
46
- RubyPgExtras.sql_for(query_name: query_name)
47
- end
48
-
49
- result = connection.execute(sql)
50
-
51
- RubyPgExtras.display_result(
52
- result,
53
- title: RubyPgExtras.description_for(query_name: query_name),
31
+ RubyPgExtras.run_query_base(
32
+ query_name: query_name,
33
+ conn: connection,
34
+ exec_method: :execute,
54
35
  in_format: in_format,
36
+ args: args,
55
37
  )
56
38
  end
57
39
 
@@ -150,6 +132,16 @@ module RailsPgExtras
150
132
  end
151
133
  end
152
134
 
135
+ def self.missing_fk_indexes(args: {}, in_format: :display_table)
136
+ result = RailsPgExtras::MissingFkIndexes.call(args[:table_name])
137
+ RubyPgExtras.display_result(result, title: "Missing foreign key indexes", in_format: in_format)
138
+ end
139
+
140
+ def self.missing_fk_constraints(args: {}, in_format: :display_table)
141
+ result = RailsPgExtras::MissingFkConstraints.call(args[:table_name])
142
+ RubyPgExtras.display_result(result, title: "Missing foreign key constraints", in_format: in_format)
143
+ end
144
+
153
145
  def self.connection
154
146
  if (db_url = ENV["RAILS_PG_EXTRAS_DATABASE_URL"])
155
147
  connector = ActiveRecord::Base.establish_connection(db_url)
@@ -4,7 +4,7 @@ require "rails_pg_extras/web"
4
4
 
5
5
  module RailsPgExtras
6
6
  class Configuration
7
- DEFAULT_CONFIG = { enabled_web_actions: Web::ACTIONS - [:kill_all], public_dashboard: ENV["RAILS_PG_EXTRAS_PUBLIC_DASHBOARD"] == "true" }
7
+ DEFAULT_CONFIG = { enabled_web_actions: Web::ACTIONS - [:kill_all, :kill_pid], public_dashboard: ENV["RAILS_PG_EXTRAS_PUBLIC_DASHBOARD"] == "true" }
8
8
 
9
9
  attr_reader :enabled_web_actions
10
10
  attr_accessor :public_dashboard
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsPgExtras
4
+ class MissingFkConstraints < RubyPgExtras::MissingFkConstraints
5
+ private
6
+
7
+ def query_module
8
+ RailsPgExtras
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsPgExtras
4
+ class MissingFkIndexes < RubyPgExtras::MissingFkIndexes
5
+ private
6
+
7
+ def query_module
8
+ RailsPgExtras
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsPgExtras
4
- VERSION = "5.5.0"
4
+ VERSION = "5.6.1"
5
5
  end
@@ -2,7 +2,7 @@ require "rails_pg_extras/web/engine"
2
2
 
3
3
  module RailsPgExtras
4
4
  module Web
5
- ACTIONS = %i[kill_all pg_stat_statements_reset add_extensions].freeze
5
+ ACTIONS = %i[kill_all pg_stat_statements_reset add_extensions kill_pid].freeze
6
6
 
7
7
  def self.action_enabled?(action_name)
8
8
  RailsPgExtras.configuration.enabled_web_actions.include?(action_name.to_sym)
data/spec/smoke_spec.rb CHANGED
@@ -4,7 +4,13 @@ require "spec_helper"
4
4
  require "rails-pg-extras"
5
5
 
6
6
  describe RailsPgExtras do
7
- RailsPgExtras::QUERIES.each do |query_name|
7
+ SKIP_QUERIES = %i[
8
+ kill_all
9
+ table_schema
10
+ table_foreign_keys
11
+ ]
12
+
13
+ RailsPgExtras::QUERIES.reject { |q| SKIP_QUERIES.include?(q) }.each do |query_name|
8
14
  it "#{query_name} query can be executed" do
9
15
  expect do
10
16
  RailsPgExtras.run_query(
@@ -31,7 +37,7 @@ describe RailsPgExtras do
31
37
 
32
38
  it "collecting queries data works" do
33
39
  output = RailsPgExtras.measure_queries { RailsPgExtras.diagnose(in_format: :hash) }
34
- expect(output.fetch(:count)).to eq 10
40
+ expect(output.fetch(:count) > 0).to eq(true)
35
41
  end
36
42
 
37
43
  it "supports custom RAILS_PG_EXTRAS_DATABASE_URL" do
@@ -44,4 +50,20 @@ describe RailsPgExtras do
44
50
 
45
51
  ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = nil
46
52
  end
53
+
54
+ describe "missing_fk_indexes" do
55
+ it "works" do
56
+ expect {
57
+ RailsPgExtras.missing_fk_indexes
58
+ }.not_to raise_error
59
+ end
60
+ end
61
+
62
+ describe "missing_fk_constraints" do
63
+ it "works" do
64
+ expect {
65
+ RailsPgExtras.missing_fk_constraints
66
+ }.not_to raise_error
67
+ end
68
+ end
47
69
  end
data/spec/spec_helper.rb CHANGED
@@ -7,21 +7,16 @@ require_relative "../lib/rails-pg-extras"
7
7
 
8
8
  pg_version = ENV["PG_VERSION"]
9
9
 
10
- port = if pg_version == "12"
11
- "5432"
12
- elsif pg_version == "13"
13
- "5433"
14
- elsif pg_version == "14"
15
- "5434"
16
- elsif pg_version == "15"
17
- "5435"
18
- elsif pg_version == "16"
19
- "5436"
20
- elsif pg_version == "17"
21
- "5437"
22
- else
23
- "5432"
24
- end
10
+ PG_PORTS = {
11
+ "12" => "5432",
12
+ "13" => "5433",
13
+ "14" => "5434",
14
+ "15" => "5435",
15
+ "16" => "5436",
16
+ "17" => "5437",
17
+ }
18
+
19
+ port = PG_PORTS.fetch(pg_version, "5432")
25
20
 
26
21
  ENV["DATABASE_URL"] ||= "postgresql://postgres:secret@localhost:#{port}/rails-pg-extras-test"
27
22
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-pg-extras
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-04 00:00:00.000000000 Z
11
+ date: 2025-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-pg-extras
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.5.0
19
+ version: 5.6.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.5.0
26
+ version: 5.6.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,8 @@ files:
116
116
  - lib/rails_pg_extras/diagnose_print.rb
117
117
  - lib/rails_pg_extras/index_info.rb
118
118
  - lib/rails_pg_extras/index_info_print.rb
119
+ - lib/rails_pg_extras/missing_fk_constraints.rb
120
+ - lib/rails_pg_extras/missing_fk_indexes.rb
119
121
  - lib/rails_pg_extras/railtie.rb
120
122
  - lib/rails_pg_extras/table_info.rb
121
123
  - lib/rails_pg_extras/table_info_print.rb