rails-pg-extras 5.5.0 → 5.6.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: 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