rails-pg-extras 5.6.10 → 5.6.12

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: 2207f16892a3e8d48f92833a2388f4e194b27b2e365f41f6461112510828c2d8
4
- data.tar.gz: f4cde7b6f204d740f520b87b1c3cd04f40d199680adc103e7611fb18ba2caa29
3
+ metadata.gz: 0ab5f408e9c0a7f70fea16a4d8d2e5079b130c8bb3536a2da760aa8cc509b1cf
4
+ data.tar.gz: 39a83a3ce09da9710884277245c2306ab70b3584f32885651f4553a7faf0ebbb
5
5
  SHA512:
6
- metadata.gz: 0e0866a4c4a75e3a095c5956163143cc1d451099bfd6a6cd2620b2073439558dcd0510458bb358164b5cdd3f4d12a862cf251571f6885ffb044b93a98db1ef77
7
- data.tar.gz: 3c6dc4a9f5e147c692c64160538ba95749eeb4f9abbb3998a73df7f05c4b8824487c31481ad84511c007a70fffe5f6fd187dde61dd0c57853981c3c8b9de066e
6
+ metadata.gz: 23302e3cf2afab6463cf306dc9d7274d0da0364080e105559260b47f93ec3c55bfb7faed12185f7119a6b4a64fb64568ce637e03a8cef4188fe983039753218d
7
+ data.tar.gz: 00bf2ca5b4c857ba53063f3063501d5d2d4a5b561256aef79de379b921461d041e5e50bf4667480d06a2aafad986c8c95b2437ca0cd0f87b457f62e6388104e2
data/README.md CHANGED
@@ -12,6 +12,10 @@ Optionally you can enable a visual interface:
12
12
 
13
13
  ![Web interface](https://github.com/pawurb/rails-pg-extras/raw/main/pg-extras-ui-3.png)
14
14
 
15
+ [rails-pg-extras-mcp gem](https://github.com/pawurb/rails-pg-extras-mcp) provides an MCP (Model Context Protocol) interface enabling PostgreSQL metadata and performance analysis with an LLM support.
16
+
17
+ ![LLM interface](https://github.com/pawurb/rails-pg-extras/raw/main/pg-extras-mcp.png)
18
+
15
19
  Alternative versions:
16
20
 
17
21
  - Core dependency - [Ruby](https://github.com/pawurb/ruby-pg-extras)
@@ -59,6 +63,12 @@ By default a primary ActiveRecord database connection is used for running metada
59
63
  ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = "postgresql://postgres:secret@localhost:5432/database_name"
60
64
  ```
61
65
 
66
+ Alternatively, you can specify database URL with a method call:
67
+
68
+ ```ruby
69
+ RailsPgExtras.database_url = "postgresql://postgres:secret@localhost:5432/database_name"
70
+ ```
71
+
62
72
  ## Usage
63
73
 
64
74
  Each command can be used as a rake task, or a directly from the Ruby code.
@@ -453,18 +463,18 @@ RailsPgExtras.outliers(args: { limit: 20 })
453
463
 
454
464
  $ rake pg_extras:outliers
455
465
 
456
- qry | exec_time | prop_exec_time | ncalls | sync_io_time
457
- -----------------------------------------+------------------+----------------+-------------+--------------
458
- SELECT * FROM archivable_usage_events.. | 154:39:26.431466 | 72.2% | 34,211,877 | 00:00:00
459
- COPY public.archivable_usage_events (.. | 50:38:33.198418 | 23.6% | 13 | 13:34:21.00108
460
- COPY public.usage_events (id, reporte.. | 02:32:16.335233 | 1.2% | 13 | 00:34:19.784318
461
- INSERT INTO usage_events (id, retaine.. | 01:42:59.436532 | 0.8% | 12,328,187 | 00:00:00
462
- SELECT * FROM usage_events WHERE (alp.. | 01:18:10.754354 | 0.6% | 102,114,301 | 00:00:00
463
- UPDATE usage_events SET reporter_id =.. | 00:52:35.683254 | 0.4% | 23,786,348 | 00:00:00
466
+ qry | exec_time | prop_exec_time | ncalls | sync_io_time | avg_exec_ms
467
+ -----------------------------------------+------------------+----------------+-------------+--------------+-------------
468
+ SELECT * FROM archivable_usage_events.. | 154:39:26.431466 | 72.2% | 34,211,877 | 00:00:00 | 16.24
469
+ COPY public.archivable_usage_events (.. | 50:38:33.198418 | 23.6% | 13 | 13:34:21.008 | 233845.63
470
+ COPY public.usage_events (id, reporte.. | 02:32:16.335233 | 1.2% | 13 | 00:34:19.718 | 11689.72
471
+ INSERT INTO usage_events (id, retaine.. | 01:42:59.436532 | 0.8% | 12,328,187 | 00:00:00 | 0.84
472
+ SELECT * FROM usage_events WHERE (alp.. | 01:18:10.754354 | 0.6% | 102,114,301 | 00:00:00 | 0.77
473
+ UPDATE usage_events SET reporter_id =.. | 00:52:35.683254 | 0.4% | 23,786,348 | 00:00:00 | 0.13
464
474
  (truncated results for brevity)
465
475
  ```
466
476
 
467
- This command displays statements, obtained from `pg_stat_statements`, ordered by the amount of time to execute in aggregate. This includes the statement itself, the total execution time for that statement, the proportion of total execution time for all statements that statement has taken up, the number of times that statement has been called, and the amount of time that statement spent on synchronous I/O (reading/writing from the file system).
477
+ This command displays statements, obtained from `pg_stat_statements`, ordered by the amount of time to execute in aggregate. This includes the statement itself, the total execution time for that statement, the proportion of total execution time for all statements that statement has taken up, the number of times that statement has been called, the amount of time that statement spent on synchronous I/O (reading/writing from the file system), and the average execution time per call in milliseconds.
468
478
 
469
479
  Typically, an efficient query will have an appropriate ratio of calls to total execution time, with as little time spent on I/O as possible. Queries that have a high total execution time but low call count should be investigated to improve their performance. Queries that have a high proportion of execution time being spent on synchronous I/O should also be investigated.
470
480
 
@@ -477,17 +487,17 @@ RailsPgExtras.calls(args: { limit: 10 })
477
487
 
478
488
  $ rake pg_extras:calls
479
489
 
480
- qry | exec_time | prop_exec_time | ncalls | sync_io_time
481
- -----------------------------------------+------------------+----------------+-------------+--------------
482
- SELECT * FROM usage_events WHERE (alp.. | 01:18:11.073333 | 0.6% | 102,120,780 | 00:00:00
483
- BEGIN | 00:00:51.285988 | 0.0% | 47,288,662 | 00:00:00
484
- COMMIT | 00:00:52.31724 | 0.0% | 47,288,615 | 00:00:00
485
- SELECT * FROM archivable_usage_event.. | 154:39:26.431466 | 72.2% | 34,211,877 | 00:00:00
486
- UPDATE usage_events SET reporter_id =.. | 00:52:35.986167 | 0.4% | 23,788,388 | 00:00:00
490
+ qry | exec_time | prop_exec_time | ncalls | sync_io_time | avg_exec_ms
491
+ -----------------------------------------+------------------+----------------+-------------+--------------+-------------
492
+ SELECT * FROM usage_events WHERE (alp.. | 01:18:11.073333 | 0.6% | 102,120,780 | 00:00:00 | 1
493
+ BEGIN | 00:00:51.285988 | 0.0% | 47,288,662 | 00:00:00 | 0
494
+ COMMIT | 00:00:52.31724 | 0.0% | 47,288,615 | 00:00:00 | 0
495
+ SELECT * FROM archivable_usage_event.. | 154:39:26.431466 | 72.2% | 34,211,877 | 00:00:00 | 16
496
+ UPDATE usage_events SET reporter_id =.. | 00:52:35.986167 | 0.4% | 23,788,388 | 00:00:00 | 0
487
497
  (truncated results for brevity)
488
498
  ```
489
499
 
490
- This command is much like `pg:outliers`, but ordered by the number of times a statement has been called.
500
+ This command is much like `pg:outliers`, but ordered by the number of times a statement has been called.
491
501
 
492
502
  [More info](https://pawelurbanek.com/postgresql-fix-performance#missing-indexes)
493
503
 
@@ -3,10 +3,8 @@
3
3
 
4
4
  <br>
5
5
 
6
- <%=
7
- link_to " Back to Diagnose", queries_path,
8
- class: "inline-block bg-blue-500 text-white font-medium px-4 py-2 rounded-lg shadow-md hover:bg-blue-600 transition"
9
- %>
6
+ <%= link_to "← Back to Diagnose", queries_path,
7
+ class: "inline-block bg-blue-500 text-white font-medium px-4 py-2 rounded-lg shadow-md hover:bg-blue-600 transition" %>
10
8
 
11
9
  <% if @error %>
12
10
  <div class="text-red-500 p-3 font-mono my-5"><%= @error %></div>
@@ -1,7 +1,7 @@
1
- <%= form_tag queries_path, id: "queries", method: :get do |f| %>
2
- <%= select_tag :query_name, options_for_select(@all_queries, params[:query_name]),
3
- { prompt: "diagnose", class: "border p-2 font-bold", autofocus: true } %>
4
- <% end %>
1
+ <%= form_tag queries_path, id: "queries", method: :get do |f| %>
2
+ <%= select_tag :query_name, options_for_select(@all_queries, params[:query_name]),
3
+ { prompt: "diagnose", class: "border p-2 font-bold", autofocus: true } %>
4
+ <% end %>
5
5
 
6
6
  <%= javascript_tag nonce: true do -%>
7
7
  document.getElementById('queries').addEventListener('change', (e) => {
@@ -12,6 +12,7 @@ require "rails_pg_extras/table_info"
12
12
  require "rails_pg_extras/table_info_print"
13
13
 
14
14
  module RailsPgExtras
15
+ @@database_url = nil
15
16
  QUERIES = RubyPgExtras::QUERIES
16
17
  DEFAULT_ARGS = RubyPgExtras::DEFAULT_ARGS
17
18
  NEW_PG_STAT_STATEMENTS = RubyPgExtras::NEW_PG_STAT_STATEMENTS
@@ -142,8 +143,14 @@ module RailsPgExtras
142
143
  RubyPgExtras.display_result(result, title: "Missing foreign key constraints", in_format: in_format)
143
144
  end
144
145
 
146
+ def self.database_url=(value)
147
+ @@database_url = value
148
+ end
149
+
145
150
  def self.connection
146
- if (db_url = ENV["RAILS_PG_EXTRAS_DATABASE_URL"])
151
+ db_url = @@database_url || ENV["RAILS_PG_EXTRAS_DATABASE_URL"]
152
+
153
+ if db_url.present?
147
154
  connector = ActiveRecord::Base.establish_connection(db_url)
148
155
 
149
156
  if connector.respond_to?(:connection)
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fast_mcp"
4
+ require "rack"
5
+ require "rails_pg_extras/version"
6
+
7
+ SKIP_QUERIES = %i[
8
+ add_extensions
9
+ pg_stat_statements_reset
10
+ kill_pid
11
+ kill_all
12
+ mandelbrot
13
+ ]
14
+
15
+ QUERY_TOOL_CLASSES = RubyPgExtras::QUERIES.reject { |q| SKIP_QUERIES.include?(q) }.map do |query_name|
16
+ Class.new(FastMcp::Tool) do
17
+ description RubyPgExtras.description_for(query_name: query_name)
18
+
19
+ define_method :call do
20
+ RailsPgExtras.public_send(query_name, in_format: :hash)
21
+ end
22
+
23
+ define_singleton_method :name do
24
+ query_name.to_s
25
+ end
26
+ end
27
+ end
28
+
29
+ class MissingFkConstraintsTool < FastMcp::Tool
30
+ description "Shows missing foreign key constraints"
31
+
32
+ def call
33
+ RailsPgExtras.missing_fk_constraints(in_format: :hash)
34
+ end
35
+
36
+ def self.name
37
+ "missing_fk_constraints"
38
+ end
39
+ end
40
+
41
+ class MissingFkIndexesTool < FastMcp::Tool
42
+ description "Shows missing foreign key indexes"
43
+
44
+ def call
45
+ RailsPgExtras.missing_fk_indexes(in_format: :hash)
46
+ end
47
+
48
+ def self.name
49
+ "missing_fk_indexes"
50
+ end
51
+ end
52
+
53
+ class DiagnoseTool < FastMcp::Tool
54
+ description "Performs a health check of the database"
55
+
56
+ def call
57
+ RailsPgExtras.diagnose(in_format: :hash)
58
+ end
59
+
60
+ def self.name
61
+ "diagnose"
62
+ end
63
+ end
64
+
65
+ class ReadmeResource < FastMcp::Resource
66
+ uri "file://README.md"
67
+ resource_name "README"
68
+ description "The README for RailsPgExtras"
69
+ mime_type "text/plain"
70
+
71
+ def content
72
+ File.read(uri)
73
+ end
74
+ end
75
+
76
+ module RailsPgExtras
77
+ class MCPApp
78
+ def self.build
79
+ app = lambda do |_env|
80
+ [200, { "Content-Type" => "text/html" },
81
+ ["<html><body><h1>Hello from Rack!</h1><p>This is a simple Rack app with MCP middleware.</p></body></html>"]]
82
+ end
83
+
84
+ # Create the MCP middleware
85
+ mcp_app = FastMcp.rack_middleware(
86
+ app,
87
+ name: "rails-pg-extras", version: RailsPgExtras::VERSION,
88
+ path_prefix: "/pg-extras-mcp",
89
+ logger: Logger.new($stdout),
90
+ ) do |server|
91
+ server.register_tools(DiagnoseTool)
92
+ server.register_tools(MissingFkConstraintsTool)
93
+ server.register_tools(MissingFkIndexesTool)
94
+ server.register_tools(*QUERY_TOOL_CLASSES)
95
+
96
+ server.register_resource(ReadmeResource)
97
+
98
+ Rack::Builder.new { run mcp_app }
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsPgExtras
4
- VERSION = "5.6.10"
4
+ VERSION = "5.6.12"
5
5
  end
data/pg-extras-mcp.png ADDED
Binary file
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.6.10
4
+ version: 5.6.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-07 00:00:00.000000000 Z
11
+ date: 2025-07-13 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.6.10
19
+ version: 5.6.12
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.6.10
26
+ version: 5.6.12
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,7 @@ 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/mcp_app.rb
119
120
  - lib/rails_pg_extras/missing_fk_constraints.rb
120
121
  - lib/rails_pg_extras/missing_fk_indexes.rb
121
122
  - lib/rails_pg_extras/railtie.rb
@@ -126,6 +127,7 @@ files:
126
127
  - lib/rails_pg_extras/web.rb
127
128
  - lib/rails_pg_extras/web/engine.rb
128
129
  - marginalia-logs.png
130
+ - pg-extras-mcp.png
129
131
  - pg-extras-ui-3.png
130
132
  - rails-pg-extras-diagnose.png
131
133
  - rails-pg-extras.gemspec