pgdexter 0.5.0 → 0.5.2
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/CHANGELOG.md +11 -0
- data/LICENSE.txt +1 -1
- data/README.md +4 -25
- data/lib/dexter/indexer.rb +26 -12
- data/lib/dexter/query.rb +1 -1
- data/lib/dexter/version.rb +1 -1
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e51edc18f4049b9ca811e4662248aa7a23e748b000975a87fa4a431f49ec632b
|
4
|
+
data.tar.gz: f3aa243431e0aab1d84d4cbc48ee6a329581acedc7a9c70e2a13e7bb0e15b529
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67b04a3c336c3bec239490a386825153699c646a01f5fe30d84b42445695f2431b1545bace72c5b955e03e92ad531810b5a73971d559abb06f175a1965454b89
|
7
|
+
data.tar.gz: 63691b01566b54056201c2d0a5c73a8153252b7c2ce4619983feb3b20e58d49d4a738b210be61d46231d3b5c78c1787664c625c0eb0216ee0d9ba19dcc8a7621
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 0.5.2 (2024-01-10)
|
2
|
+
|
3
|
+
- Added Docker image for `linux/arm64`
|
4
|
+
- Switched to `GENERIC_PLAN` for Postgres 16
|
5
|
+
- Fixed error with `auto_explain`
|
6
|
+
- Fixed warning with Ruby 3.3
|
7
|
+
|
8
|
+
## 0.5.1 (2023-05-27)
|
9
|
+
|
10
|
+
- Fixed `JSON::NestingError`
|
11
|
+
|
1
12
|
## 0.5.0 (2023-04-18)
|
2
13
|
|
3
14
|
- Added support for normalized queries
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@ The automatic indexer for Postgres
|
|
4
4
|
|
5
5
|
[Read about how it works](https://ankane.org/introducing-dexter) or [watch the talk](https://www.youtube.com/watch?v=Mni_1yTaNbE)
|
6
6
|
|
7
|
-
[](https://github.com/ankane/dexter/actions)
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -12,8 +12,8 @@ First, install [HypoPG](https://github.com/HypoPG/hypopg) on your database serve
|
|
12
12
|
|
13
13
|
```sh
|
14
14
|
cd /tmp
|
15
|
-
curl -L https://github.com/HypoPG/hypopg/archive/1.
|
16
|
-
cd hypopg-1.
|
15
|
+
curl -L https://github.com/HypoPG/hypopg/archive/1.4.0.tar.gz | tar xz
|
16
|
+
cd hypopg-1.4.0
|
17
17
|
make
|
18
18
|
make install # may need sudo
|
19
19
|
```
|
@@ -174,26 +174,6 @@ When streaming logs, specify the time to wait between processing queries
|
|
174
174
|
dexter --interval 60 # seconds
|
175
175
|
```
|
176
176
|
|
177
|
-
## Examples
|
178
|
-
|
179
|
-
Postgres package on Ubuntu 22.04
|
180
|
-
|
181
|
-
```sh
|
182
|
-
sudo -u postgres dexter -d dbname /var/log/postgresql/postgresql-14-main.log
|
183
|
-
```
|
184
|
-
|
185
|
-
Homebrew Postgres on Mac ARM
|
186
|
-
|
187
|
-
```sh
|
188
|
-
dexter -d dbname /opt/homebrew/var/log/postgresql@14.log
|
189
|
-
```
|
190
|
-
|
191
|
-
Homebrew Postgres on Mac x86-64
|
192
|
-
|
193
|
-
```sh
|
194
|
-
dexter -d dbname /usr/local/var/log/postgresql@14.log
|
195
|
-
```
|
196
|
-
|
197
177
|
## Analyze
|
198
178
|
|
199
179
|
For best results, make sure your tables have been recently analyzed so statistics are up-to-date. You can ask Dexter to analyze tables it comes across that haven’t been analyzed in the past hour with:
|
@@ -230,9 +210,8 @@ The `hypopg` extension, which Dexter needs to run, is available on [these provid
|
|
230
210
|
|
231
211
|
For other providers, see [this guide](guides/Hosted-Postgres.md). To request a new extension:
|
232
212
|
|
233
|
-
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
|
234
213
|
- Google Cloud SQL - vote or comment on [this page](https://issuetracker.google.com/issues/69250435)
|
235
|
-
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/
|
214
|
+
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/managed-database/p/support-hypopg-for-postgres)
|
236
215
|
|
237
216
|
## HypoPG Installation Notes
|
238
217
|
|
data/lib/dexter/indexer.rb
CHANGED
@@ -185,7 +185,7 @@ module Dexter
|
|
185
185
|
|
186
186
|
# get initial costs for queries
|
187
187
|
calculate_plan(queries)
|
188
|
-
explainable_queries = queries.select { |q| q.
|
188
|
+
explainable_queries = queries.select { |q| q.plans.any? && q.high_cost? }
|
189
189
|
|
190
190
|
# filter tables for performance
|
191
191
|
tables = Set.new(explainable_queries.flat_map(&:tables))
|
@@ -197,10 +197,16 @@ module Dexter
|
|
197
197
|
possible_columns = Set.new
|
198
198
|
explainable_queries.each do |query|
|
199
199
|
log "Finding columns: #{query.statement}" if @log_level == "debug3"
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
200
|
+
begin
|
201
|
+
find_columns(query.tree).each do |col|
|
202
|
+
last_col = col["fields"].last
|
203
|
+
if last_col["String"]
|
204
|
+
possible_columns << last_col["String"]["sval"]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
rescue JSON::NestingError
|
208
|
+
if @log_level.start_with?("debug")
|
209
|
+
log colorize("ERROR: Cannot get columns", :red)
|
204
210
|
end
|
205
211
|
end
|
206
212
|
end
|
@@ -226,7 +232,7 @@ module Dexter
|
|
226
232
|
end
|
227
233
|
|
228
234
|
def find_columns(plan)
|
229
|
-
plan = JSON.parse(plan.to_json)
|
235
|
+
plan = JSON.parse(plan.to_json, max_nesting: 1000)
|
230
236
|
find_by_key(plan, "ColumnRef")
|
231
237
|
end
|
232
238
|
|
@@ -515,8 +521,8 @@ module Dexter
|
|
515
521
|
raise Dexter::Abort, e.message
|
516
522
|
end
|
517
523
|
|
518
|
-
def execute(query, pretty: true, params: [])
|
519
|
-
# use exec_params instead of exec for security
|
524
|
+
def execute(query, pretty: true, params: [], use_exec: false)
|
525
|
+
# use exec_params instead of exec when possible for security
|
520
526
|
#
|
521
527
|
# Unlike PQexec, PQexecParams allows at most one SQL command in the given string.
|
522
528
|
# (There can be semicolons in it, but not more than one nonempty command.)
|
@@ -527,7 +533,11 @@ module Dexter
|
|
527
533
|
log colorize("[sql] #{query}#{params.any? ? " /*#{params.to_json}*/" : ""}", :cyan) if @log_sql
|
528
534
|
|
529
535
|
@mutex.synchronize do
|
530
|
-
|
536
|
+
if use_exec
|
537
|
+
conn.exec("#{query} /*dexter*/").to_a
|
538
|
+
else
|
539
|
+
conn.exec_params("#{query} /*dexter*/", params).to_a
|
540
|
+
end
|
531
541
|
end
|
532
542
|
end
|
533
543
|
|
@@ -537,7 +547,9 @@ module Dexter
|
|
537
547
|
|
538
548
|
# try to EXPLAIN normalized queries
|
539
549
|
# https://dev.to/yugabyte/explain-from-pgstatstatements-normalized-queries-how-to-always-get-the-generic-plan-in--5cfi
|
540
|
-
|
550
|
+
normalized = query.include?("$1")
|
551
|
+
generic_plan = normalized && server_version_num >= 160000
|
552
|
+
explain_normalized = normalized && !generic_plan
|
541
553
|
if explain_normalized
|
542
554
|
prepared_name = "dexter_prepared"
|
543
555
|
execute("PREPARE #{prepared_name} AS #{safe_statement(query)}", pretty: false)
|
@@ -560,12 +572,14 @@ module Dexter
|
|
560
572
|
end
|
561
573
|
end
|
562
574
|
|
575
|
+
explain_prefix = generic_plan ? "GENERIC_PLAN, " : ""
|
576
|
+
|
563
577
|
# strip semi-colons as another measure of defense
|
564
|
-
plan = JSON.parse(execute("EXPLAIN (FORMAT JSON) #{safe_statement(query)}", pretty: false).first["QUERY PLAN"], max_nesting: 1000).first["Plan"]
|
578
|
+
plan = JSON.parse(execute("EXPLAIN (#{explain_prefix}FORMAT JSON) #{safe_statement(query)}", pretty: false, use_exec: generic_plan).first["QUERY PLAN"], max_nesting: 1000).first["Plan"]
|
565
579
|
|
566
580
|
if @log_explain
|
567
581
|
# Pass format to prevent ANALYZE
|
568
|
-
puts execute("EXPLAIN (FORMAT TEXT) #{safe_statement(query)}", pretty: false).map { |r| r["QUERY PLAN"] }.join("\n")
|
582
|
+
puts execute("EXPLAIN (#{explain_prefix}FORMAT TEXT) #{safe_statement(query)}", pretty: false, use_exec: generic_plan).map { |r| r["QUERY PLAN"] }.join("\n")
|
569
583
|
end
|
570
584
|
|
571
585
|
plan
|
data/lib/dexter/query.rb
CHANGED
data/lib/dexter/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgdexter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: csv
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: pg
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
110
|
- !ruby/object:Gem::Version
|
97
111
|
version: '0'
|
98
112
|
requirements: []
|
99
|
-
rubygems_version: 3.
|
113
|
+
rubygems_version: 3.5.3
|
100
114
|
signing_key:
|
101
115
|
specification_version: 4
|
102
116
|
summary: The automatic indexer for Postgres
|