rails-pg-extras 5.6.14 → 5.6.16
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 +4 -4
- data/.github/workflows/ci.yml +12 -12
- data/README.md +72 -9
- data/app/views/rails_pg_extras/web/queries/show.html.erb +2 -1
- data/docker-compose.yml.sample +10 -10
- data/lib/rails-pg-extras.rb +66 -44
- data/lib/rails_pg_extras/configuration.rb +5 -1
- data/lib/rails_pg_extras/version.rb +1 -1
- data/spec/smoke_spec.rb +43 -2
- data/spec/spec_helper.rb +2 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 714a0f5659226e30d168cac200be9fa570ff608977b2ac8400a13e18a1c1e1f5
|
|
4
|
+
data.tar.gz: 803ec924433eeda21828d30ff23524007a108db4dfc02d83002fb4b0b2527fc6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0da5717b4d0483ff82707da9ba19715e2d6d8a26feb46d77e0fae3a41a75d9c7dbc03f1881998752f6ece95984c4db408050218fc87bea43731ef3ebe1f0dc5
|
|
7
|
+
data.tar.gz: 1d72247f4fed436a0220051b98ba8e0ea619b2f12737c2958c895c73d8e19050a707d3baa96479de968248f5aa431a532f9aee358e533322727d12f2c9fa1ec7
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -15,13 +15,6 @@ jobs:
|
|
|
15
15
|
ruby-version: ['3.4', '3.3', '3.2', '3.1', '3.0', '2.7']
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v4
|
|
18
|
-
- name: Run PostgreSQL 12
|
|
19
|
-
run: |
|
|
20
|
-
docker run --env POSTGRES_USER=postgres \
|
|
21
|
-
--env POSTGRES_DB=rails-pg-extras-test \
|
|
22
|
-
--env POSTGRES_PASSWORD=secret \
|
|
23
|
-
-d -p 5432:5432 postgres:12.20-alpine \
|
|
24
|
-
postgres -c shared_preload_libraries=pg_stat_statements
|
|
25
18
|
- name: Run PostgreSQL 13
|
|
26
19
|
run: |
|
|
27
20
|
docker run --env POSTGRES_USER=postgres \
|
|
@@ -57,6 +50,13 @@ jobs:
|
|
|
57
50
|
--env POSTGRES_PASSWORD=secret \
|
|
58
51
|
-d -p 5437:5432 postgres:17.0-alpine \
|
|
59
52
|
postgres -c shared_preload_libraries=pg_stat_statements
|
|
53
|
+
- name: Run PostgreSQL 18
|
|
54
|
+
run: |
|
|
55
|
+
docker run --env POSTGRES_USER=postgres \
|
|
56
|
+
--env POSTGRES_DB=rails-pg-extras-test \
|
|
57
|
+
--env POSTGRES_PASSWORD=secret \
|
|
58
|
+
-d -p 5438:5432 postgres:18.1-alpine \
|
|
59
|
+
postgres -c shared_preload_libraries=pg_stat_statements
|
|
60
60
|
sleep 5
|
|
61
61
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
62
62
|
uses: ruby/setup-ruby@v1
|
|
@@ -71,11 +71,6 @@ jobs:
|
|
|
71
71
|
bundle config set --local path 'vendor/bundle'
|
|
72
72
|
bundle install
|
|
73
73
|
sleep 10
|
|
74
|
-
- name: Run tests for PG 12
|
|
75
|
-
env:
|
|
76
|
-
PG_VERSION: 12
|
|
77
|
-
run: |
|
|
78
|
-
bundle exec rspec spec/
|
|
79
74
|
- name: Run tests for PG 13
|
|
80
75
|
env:
|
|
81
76
|
PG_VERSION: 13
|
|
@@ -101,4 +96,9 @@ jobs:
|
|
|
101
96
|
PG_VERSION: 17
|
|
102
97
|
run: |
|
|
103
98
|
bundle exec rspec spec/
|
|
99
|
+
- name: Run tests for PG 18
|
|
100
|
+
env:
|
|
101
|
+
PG_VERSION: 18
|
|
102
|
+
run: |
|
|
103
|
+
bundle exec rspec spec/
|
|
104
104
|
|
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Optionally you can enable a visual interface:
|
|
|
12
12
|
|
|
13
13
|

|
|
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.
|
|
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
16
|
|
|
17
17
|

|
|
18
18
|
|
|
@@ -57,16 +57,27 @@ You should see the similar line in the output:
|
|
|
57
57
|
RailsPgExtras.add_extensions
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
By default
|
|
60
|
+
By default rails-pg-extras uses your app’s default `ActiveRecord::Base.connection` (typically `primary`) for running metadata queries, rake tasks and the web UI.
|
|
61
|
+
|
|
62
|
+
If your app uses Rails multiple databases, the web UI can switch connections dynamically. It reads the available database names from `ActiveRecord::Base.configurations` for the current environment and selects one via the `db_key` query param (thread-local per request, so it’s safe under concurrency):
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# examples
|
|
66
|
+
/pg_extras?db_key=primary
|
|
67
|
+
/pg_extras?db_key=animals
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
To connect to a database that isn’t defined in `database.yml` (or when using rake tasks / Ruby API outside the web UI), you can also provide an explicit URL via `ENV['RAILS_PG_EXTRAS_DATABASE_CONFIG']`:
|
|
61
71
|
|
|
62
72
|
```ruby
|
|
63
|
-
ENV["
|
|
73
|
+
ENV["RAILS_PG_EXTRAS_DATABASE_CONFIG"] = "postgresql://postgres:secret@localhost:5432/database_name"
|
|
64
74
|
```
|
|
65
75
|
|
|
66
|
-
Alternatively, you can specify database
|
|
76
|
+
Alternatively, you can specify database configuration with a method call:
|
|
67
77
|
|
|
68
78
|
```ruby
|
|
69
|
-
RailsPgExtras.
|
|
79
|
+
RailsPgExtras.database_config = "postgresql://postgres:secret@localhost:5432/database_name"
|
|
80
|
+
RailsPgExtras.database_config = :rails_pg_extras
|
|
70
81
|
```
|
|
71
82
|
|
|
72
83
|
## Usage
|
|
@@ -158,6 +169,24 @@ RailsPgExtras.configure do |config|
|
|
|
158
169
|
end
|
|
159
170
|
```
|
|
160
171
|
|
|
172
|
+
You can also configure default ignore lists for the missing foreign key checkers. This helps skip columns that you know should not be considered foreign keys (constraints) or you intentionally do not want to index (indexes).
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
RailsPgExtras.configure do |config|
|
|
176
|
+
# Accepts an Array or a comma-separated String of entries like:
|
|
177
|
+
# - "posts.category_id" (ignore a specific table+column)
|
|
178
|
+
# - "category_id" (ignore this column name for all tables)
|
|
179
|
+
# - "posts.*" (ignore all columns on a table)
|
|
180
|
+
# - "*" (ignore everything)
|
|
181
|
+
config.missing_fk_constraints_ignore_list = ["posts.category_id", "category_id"]
|
|
182
|
+
config.missing_fk_indexes_ignore_list = ["feedbacks.team_id", "legacy_id"]
|
|
183
|
+
|
|
184
|
+
# Or as a comma-separated string:
|
|
185
|
+
# config.missing_fk_constraints_ignore_list = "posts.category_id, category_id"
|
|
186
|
+
# config.missing_fk_indexes_ignore_list = "feedbacks.team_id, legacy_id"
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
161
190
|
## Available methods
|
|
162
191
|
|
|
163
192
|
### `measure_queries`
|
|
@@ -222,7 +251,7 @@ you can add this info to the output:
|
|
|
222
251
|
|
|
223
252
|
### `missing_fk_indexes`
|
|
224
253
|
|
|
225
|
-
This method lists
|
|
254
|
+
This method lists **actual foreign key columns** (based on existing foreign key constraints) which don't have a supporting index. It's recommended to always index foreign key columns because they are commonly used for lookups and join conditions.
|
|
226
255
|
|
|
227
256
|
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.
|
|
228
257
|
|
|
@@ -242,12 +271,32 @@ RailsPgExtras.missing_fk_indexes(args: { table_name: "users" })
|
|
|
242
271
|
|
|
243
272
|
`table_name` argument is optional, if omitted, the method will display missing fk indexes for all the tables.
|
|
244
273
|
|
|
274
|
+
You can also exclude known/intentional cases using `ignore_list` (array or comma-separated string), with entries like:
|
|
275
|
+
- "posts.category_id" (ignore a specific table+column)
|
|
276
|
+
- "category_id" (ignore this column name for all tables)
|
|
277
|
+
- "posts.*" (ignore all columns on a table)
|
|
278
|
+
- "*" (ignore everything)
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
RailsPgExtras.missing_fk_indexes(args: { table_name: "users", ignore_list: ["feedbacks.team_id", "posts.*"] })
|
|
282
|
+
```
|
|
283
|
+
|
|
245
284
|
## `missing_fk_constraints`
|
|
246
285
|
|
|
247
|
-
|
|
286
|
+
This method shows **columns that look like foreign keys** but don't have a corresponding foreign key constraint yet. 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).
|
|
287
|
+
|
|
288
|
+
Heuristic notes:
|
|
289
|
+
- A column is considered a candidate if it matches `<table_singular>_id` and the related table exists (underscored prefixes like `account_user_id` are supported).
|
|
290
|
+
- Rails polymorphic associations (`<name>_id` + `<name>_type`) are ignored since they cannot be expressed as real FK constraints.
|
|
291
|
+
|
|
292
|
+
You can also exclude known/intentional cases using `ignore_list` (array or comma-separated string), with entries like:
|
|
293
|
+
- "posts.category_id" (ignore a specific table+column)
|
|
294
|
+
- "category_id" (ignore this column name for all tables)
|
|
295
|
+
- "posts.*" (ignore all columns on a table)
|
|
296
|
+
- "*" (ignore everything)
|
|
248
297
|
|
|
249
298
|
```ruby
|
|
250
|
-
RailsPgExtras.missing_fk_constraints(args: { table_name: "users" })
|
|
299
|
+
RailsPgExtras.missing_fk_constraints(args: { table_name: "users", ignore_list: ["users.customer_id", "posts.*"] })
|
|
251
300
|
|
|
252
301
|
+---------------------------------+
|
|
253
302
|
| Missing foreign key constraints |
|
|
@@ -262,6 +311,20 @@ RailsPgExtras.missing_fk_constraints(args: { table_name: "users" })
|
|
|
262
311
|
|
|
263
312
|
`table_name` argument is optional, if omitted, method will display missing fk constraints for all the tables.
|
|
264
313
|
|
|
314
|
+
Examples:
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
# Per-call ignore list
|
|
318
|
+
RailsPgExtras.missing_fk_constraints(args: {
|
|
319
|
+
ignore_list: ["posts.category_id", "legacy_id", "temp_tables.*"]
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
# As a comma-separated string
|
|
323
|
+
RailsPgExtras.missing_fk_constraints(args: {
|
|
324
|
+
ignore_list: "posts.category_id, legacy_id, temp_tables.*"
|
|
325
|
+
})
|
|
326
|
+
```
|
|
327
|
+
|
|
265
328
|
### `table_schema`
|
|
266
329
|
|
|
267
330
|
This method displays structure of a selected table, listing its column names, together with types, null constraints, and default values.
|
|
@@ -497,7 +560,7 @@ $ rake pg_extras:calls
|
|
|
497
560
|
(truncated results for brevity)
|
|
498
561
|
```
|
|
499
562
|
|
|
500
|
-
This command is much like `pg:outliers`, but ordered by the number of times a statement has been called.
|
|
563
|
+
This command is much like `pg:outliers`, but ordered by the number of times a statement has been called.
|
|
501
564
|
|
|
502
565
|
[More info](https://pawelurbanek.com/postgresql-fix-performance#missing-indexes)
|
|
503
566
|
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
<br>
|
|
5
5
|
|
|
6
|
-
<%= link_to "← Back to Diagnose",
|
|
6
|
+
<%= link_to "← Back to Diagnose",
|
|
7
|
+
queries_path(params[:db_key].present? ? { db_key: params[:db_key] } : {}),
|
|
7
8
|
class: "inline-block bg-blue-500 text-white font-medium px-4 py-2 rounded-lg shadow-md hover:bg-blue-600 transition" %>
|
|
8
9
|
|
|
9
10
|
<% if @error %>
|
data/docker-compose.yml.sample
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
services:
|
|
2
|
-
postgres12:
|
|
3
|
-
image: postgres:12.20-alpine
|
|
4
|
-
command: postgres -c shared_preload_libraries=pg_stat_statements
|
|
5
|
-
environment:
|
|
6
|
-
POSTGRES_USER: postgres
|
|
7
|
-
POSTGRES_DB: rails-pg-extras-test
|
|
8
|
-
POSTGRES_PASSWORD: secret
|
|
9
|
-
ports:
|
|
10
|
-
- '5432:5432'
|
|
11
2
|
postgres13:
|
|
12
3
|
image: postgres:13.16-alpine
|
|
13
4
|
command: postgres -c shared_preload_libraries=pg_stat_statements
|
|
@@ -45,7 +36,7 @@ services:
|
|
|
45
36
|
ports:
|
|
46
37
|
- '5436:5432'
|
|
47
38
|
postgres17:
|
|
48
|
-
image: postgres:17.
|
|
39
|
+
image: postgres:17.7-alpine
|
|
49
40
|
command: postgres -c shared_preload_libraries=pg_stat_statements
|
|
50
41
|
environment:
|
|
51
42
|
POSTGRES_USER: postgres
|
|
@@ -53,4 +44,13 @@ services:
|
|
|
53
44
|
POSTGRES_PASSWORD: secret
|
|
54
45
|
ports:
|
|
55
46
|
- '5437:5432'
|
|
47
|
+
postgres18:
|
|
48
|
+
image: postgres:18.1-alpine
|
|
49
|
+
command: postgres -c shared_preload_libraries=pg_stat_statements
|
|
50
|
+
environment:
|
|
51
|
+
POSTGRES_USER: postgres
|
|
52
|
+
POSTGRES_DB: rails-pg-extras-test
|
|
53
|
+
POSTGRES_PASSWORD: secret
|
|
54
|
+
ports:
|
|
55
|
+
- '5438:5432'
|
|
56
56
|
|
data/lib/rails-pg-extras.rb
CHANGED
|
@@ -12,7 +12,10 @@ require "rails_pg_extras/table_info"
|
|
|
12
12
|
require "rails_pg_extras/table_info_print"
|
|
13
13
|
|
|
14
14
|
module RailsPgExtras
|
|
15
|
-
|
|
15
|
+
extend self
|
|
16
|
+
|
|
17
|
+
@@database_config = nil
|
|
18
|
+
|
|
16
19
|
QUERIES = RubyPgExtras::QUERIES
|
|
17
20
|
DEFAULT_ARGS = RubyPgExtras::DEFAULT_ARGS
|
|
18
21
|
NEW_PG_STAT_STATEMENTS = RubyPgExtras::NEW_PG_STAT_STATEMENTS
|
|
@@ -28,7 +31,7 @@ module RailsPgExtras
|
|
|
28
31
|
end
|
|
29
32
|
end
|
|
30
33
|
|
|
31
|
-
def
|
|
34
|
+
def run_query(query_name:, in_format:, args: {})
|
|
32
35
|
RubyPgExtras.run_query_base(
|
|
33
36
|
query_name: query_name,
|
|
34
37
|
conn: connection,
|
|
@@ -38,7 +41,7 @@ module RailsPgExtras
|
|
|
38
41
|
)
|
|
39
42
|
end
|
|
40
43
|
|
|
41
|
-
def
|
|
44
|
+
def diagnose(in_format: :display_table)
|
|
42
45
|
data = RailsPgExtras::DiagnoseData.call
|
|
43
46
|
|
|
44
47
|
if in_format == :display_table
|
|
@@ -50,14 +53,14 @@ module RailsPgExtras
|
|
|
50
53
|
end
|
|
51
54
|
end
|
|
52
55
|
|
|
53
|
-
def
|
|
56
|
+
def measure_duration(&block)
|
|
54
57
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
55
58
|
block.call
|
|
56
59
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
57
60
|
(ending - starting) * 1000
|
|
58
61
|
end
|
|
59
62
|
|
|
60
|
-
def
|
|
63
|
+
def measure_queries(&block)
|
|
61
64
|
queries = {}
|
|
62
65
|
sql_duration = 0
|
|
63
66
|
|
|
@@ -105,7 +108,7 @@ module RailsPgExtras
|
|
|
105
108
|
}
|
|
106
109
|
end
|
|
107
110
|
|
|
108
|
-
def
|
|
111
|
+
def index_info(args: {}, in_format: :display_table)
|
|
109
112
|
data = RailsPgExtras::IndexInfo.call(args[:table_name])
|
|
110
113
|
|
|
111
114
|
if in_format == :display_table
|
|
@@ -119,7 +122,7 @@ module RailsPgExtras
|
|
|
119
122
|
end
|
|
120
123
|
end
|
|
121
124
|
|
|
122
|
-
def
|
|
125
|
+
def table_info(args: {}, in_format: :display_table)
|
|
123
126
|
data = RailsPgExtras::TableInfo.call(args[:table_name])
|
|
124
127
|
|
|
125
128
|
if in_format == :display_table
|
|
@@ -133,63 +136,82 @@ module RailsPgExtras
|
|
|
133
136
|
end
|
|
134
137
|
end
|
|
135
138
|
|
|
136
|
-
def
|
|
137
|
-
|
|
139
|
+
def missing_fk_indexes(args: {}, in_format: :display_table)
|
|
140
|
+
ignore_list = args[:ignore_list]
|
|
141
|
+
ignore_list ||= RailsPgExtras.configuration.missing_fk_indexes_ignore_list
|
|
142
|
+
result = RailsPgExtras::MissingFkIndexes.call(args[:table_name], ignore_list: ignore_list)
|
|
138
143
|
RubyPgExtras.display_result(result, title: "Missing foreign key indexes", in_format: in_format)
|
|
139
144
|
end
|
|
140
145
|
|
|
141
|
-
def
|
|
142
|
-
|
|
146
|
+
def missing_fk_constraints(args: {}, in_format: :display_table)
|
|
147
|
+
ignore_list = args[:ignore_list]
|
|
148
|
+
ignore_list ||= RailsPgExtras.configuration.missing_fk_constraints_ignore_list
|
|
149
|
+
result = RailsPgExtras::MissingFkConstraints.call(args[:table_name], ignore_list: ignore_list)
|
|
143
150
|
RubyPgExtras.display_result(result, title: "Missing foreign key constraints", in_format: in_format)
|
|
144
151
|
end
|
|
145
152
|
|
|
146
|
-
def
|
|
147
|
-
@@
|
|
153
|
+
def database_config
|
|
154
|
+
@@database_config
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Deprecated
|
|
158
|
+
alias_method :database_url, :database_config
|
|
159
|
+
|
|
160
|
+
def database_config=(value)
|
|
161
|
+
@@database_config = value
|
|
148
162
|
end
|
|
149
163
|
|
|
150
|
-
|
|
164
|
+
# Deprecated
|
|
165
|
+
alias_method :database_url=, :database_config=
|
|
166
|
+
|
|
167
|
+
def connection
|
|
151
168
|
# Priority:
|
|
152
169
|
# 1) Per-request selected db (thread-local), if present -> use named configuration or URL without altering global Base
|
|
153
|
-
# 2) Explicit
|
|
170
|
+
# 2) Explicit config via setter or ENV override
|
|
154
171
|
# 3) Default ActiveRecord::Base connection
|
|
155
172
|
selected_db_key = Thread.current[:rails_pg_extras_db_key]
|
|
156
|
-
|
|
173
|
+
config = @@database_config || ENV["RAILS_PG_EXTRAS_DATABASE_CONFIG"] || ENV["RAILS_PG_EXTRAS_DATABASE_URL"] # Latter is deprecated
|
|
157
174
|
|
|
158
175
|
if selected_db_key.present?
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
thread_classes = (Thread.current[:rails_pg_extras_ar_classes] ||= {})
|
|
162
|
-
ar_class = (thread_classes[selected_db_key] ||= begin
|
|
163
|
-
if const_defined?(const_name, false)
|
|
164
|
-
const_get(const_name, false)
|
|
165
|
-
else
|
|
166
|
-
klass = Class.new(ActiveRecord::Base)
|
|
167
|
-
klass.abstract_class = true
|
|
168
|
-
const_set(const_name, klass)
|
|
169
|
-
end
|
|
170
|
-
end)
|
|
176
|
+
# Prefix thread class key to avoid collisions
|
|
177
|
+
ar_class = fetch_or_define_ar_class(thread_class_key: "database_#{selected_db_key}", const_name: selected_db_key.classify)
|
|
171
178
|
|
|
172
|
-
|
|
179
|
+
establish_connection(ar_class: ar_class, config: selected_db_key.to_sym)
|
|
180
|
+
elsif config.present?
|
|
181
|
+
ar_class = fetch_or_define_ar_class(thread_class_key: :database_config, const_name: :PgExtrasDatabaseConfig)
|
|
173
182
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
establish_connection(ar_class: ar_class, config: config)
|
|
184
|
+
else
|
|
185
|
+
ActiveRecord::Base.connection
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
# Use an isolated abstract class to avoid changing the global connection
|
|
192
|
+
def fetch_or_define_ar_class(thread_class_key:, const_name:)
|
|
193
|
+
thread_classes = Thread.current[:rails_pg_extras_ar_classes] ||= {}
|
|
183
194
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
connector.lease_connection
|
|
195
|
+
thread_classes[thread_class_key] ||=
|
|
196
|
+
if const_defined?(const_name, false)
|
|
197
|
+
const_get(const_name, false)
|
|
188
198
|
else
|
|
189
|
-
|
|
199
|
+
klass = Class.new(ActiveRecord::Base)
|
|
200
|
+
klass.abstract_class = true
|
|
201
|
+
|
|
202
|
+
const_set(const_name, klass)
|
|
190
203
|
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def establish_connection(ar_class:, config:)
|
|
207
|
+
connector = ar_class.establish_connection(config)
|
|
208
|
+
|
|
209
|
+
if connector.respond_to?(:connection)
|
|
210
|
+
connector.connection
|
|
211
|
+
elsif connector.respond_to?(:lease_connection)
|
|
212
|
+
connector.lease_connection
|
|
191
213
|
else
|
|
192
|
-
|
|
214
|
+
raise "Unsupported connector: #{connector.class}"
|
|
193
215
|
end
|
|
194
216
|
end
|
|
195
217
|
end
|
|
@@ -4,14 +4,18 @@ 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, :kill_pid], 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", missing_fk_constraints_ignore_list: [], missing_fk_indexes_ignore_list: [] }
|
|
8
8
|
|
|
9
9
|
attr_reader :enabled_web_actions
|
|
10
10
|
attr_accessor :public_dashboard
|
|
11
|
+
attr_accessor :missing_fk_constraints_ignore_list
|
|
12
|
+
attr_accessor :missing_fk_indexes_ignore_list
|
|
11
13
|
|
|
12
14
|
def initialize(attrs)
|
|
13
15
|
self.enabled_web_actions = attrs[:enabled_web_actions]
|
|
14
16
|
self.public_dashboard = attrs[:public_dashboard]
|
|
17
|
+
self.missing_fk_constraints_ignore_list = attrs[:missing_fk_constraints_ignore_list]
|
|
18
|
+
self.missing_fk_indexes_ignore_list = attrs[:missing_fk_indexes_ignore_list]
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def enabled_web_actions=(*actions)
|
data/spec/smoke_spec.rb
CHANGED
|
@@ -40,15 +40,29 @@ describe RailsPgExtras do
|
|
|
40
40
|
expect(output.fetch(:count) > 0).to eq(true)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
it "supports custom RAILS_PG_EXTRAS_DATABASE_CONFIG that specifies an URL" do
|
|
44
|
+
old_value = ENV["RAILS_PG_EXTRAS_DATABASE_CONFIG"]
|
|
45
|
+
ENV["RAILS_PG_EXTRAS_DATABASE_CONFIG"] = ENV["DATABASE_URL"]
|
|
46
|
+
puts ENV["RAILS_PG_EXTRAS_DATABASE_CONFIG"]
|
|
47
|
+
|
|
48
|
+
expect do
|
|
49
|
+
RailsPgExtras.calls
|
|
50
|
+
end.not_to raise_error
|
|
51
|
+
ensure
|
|
52
|
+
ENV["RAILS_PG_EXTRAS_DATABASE_CONFIG"] = old_value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Deprecated
|
|
43
56
|
it "supports custom RAILS_PG_EXTRAS_DATABASE_URL" do
|
|
57
|
+
old_value = ENV["RAILS_PG_EXTRAS_DATABASE_URL"]
|
|
44
58
|
ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = ENV["DATABASE_URL"]
|
|
45
59
|
puts ENV["RAILS_PG_EXTRAS_DATABASE_URL"]
|
|
46
60
|
|
|
47
61
|
expect do
|
|
48
62
|
RailsPgExtras.calls
|
|
49
63
|
end.not_to raise_error
|
|
50
|
-
|
|
51
|
-
ENV["RAILS_PG_EXTRAS_DATABASE_URL"] =
|
|
64
|
+
ensure
|
|
65
|
+
ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = old_value
|
|
52
66
|
end
|
|
53
67
|
|
|
54
68
|
describe "missing_fk_indexes" do
|
|
@@ -66,4 +80,31 @@ describe RailsPgExtras do
|
|
|
66
80
|
}.not_to raise_error
|
|
67
81
|
end
|
|
68
82
|
end
|
|
83
|
+
|
|
84
|
+
it "database_config does not affect global connection" do
|
|
85
|
+
original_connection = ActiveRecord::Base.connection
|
|
86
|
+
|
|
87
|
+
old_value = RailsPgExtras.database_config
|
|
88
|
+
RailsPgExtras.database_config = ENV["DATABASE_URL"]
|
|
89
|
+
RailsPgExtras.calls
|
|
90
|
+
|
|
91
|
+
# Verify global connection unchanged
|
|
92
|
+
expect(ActiveRecord::Base.connection).to eq(original_connection)
|
|
93
|
+
ensure
|
|
94
|
+
RailsPgExtras.database_config = old_value
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Deprecated
|
|
98
|
+
it "database_url does not affect global connection" do
|
|
99
|
+
original_connection = ActiveRecord::Base.connection
|
|
100
|
+
|
|
101
|
+
old_value = RailsPgExtras.database_url
|
|
102
|
+
RailsPgExtras.database_url = ENV["DATABASE_URL"]
|
|
103
|
+
RailsPgExtras.calls
|
|
104
|
+
|
|
105
|
+
# Verify global connection unchanged
|
|
106
|
+
expect(ActiveRecord::Base.connection).to eq(original_connection)
|
|
107
|
+
ensure
|
|
108
|
+
RailsPgExtras.database_url = old_value
|
|
109
|
+
end
|
|
69
110
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -8,15 +8,15 @@ require_relative "../lib/rails-pg-extras"
|
|
|
8
8
|
pg_version = ENV["PG_VERSION"]
|
|
9
9
|
|
|
10
10
|
PG_PORTS = {
|
|
11
|
-
"12" => "5432",
|
|
12
11
|
"13" => "5433",
|
|
13
12
|
"14" => "5434",
|
|
14
13
|
"15" => "5435",
|
|
15
14
|
"16" => "5436",
|
|
16
15
|
"17" => "5437",
|
|
16
|
+
"18" => "5438",
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
port = PG_PORTS.fetch(pg_version, "
|
|
19
|
+
port = PG_PORTS.fetch(pg_version, "5438")
|
|
20
20
|
|
|
21
21
|
ENV["DATABASE_URL"] ||= "postgresql://postgres:secret@localhost:#{port}/rails-pg-extras-test"
|
|
22
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.6.
|
|
4
|
+
version: 5.6.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- pawurb
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-10 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.
|
|
19
|
+
version: 5.6.16
|
|
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.
|
|
26
|
+
version: 5.6.16
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: railties
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|