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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65822d0d98c9641efdc3146295e098e09e83348ed109b856670a03d74ed2d70b
4
- data.tar.gz: 6d16c9019172e5e69df056ac358588151fa52ae9e8256159547e1842e2f9b97c
3
+ metadata.gz: e51edc18f4049b9ca811e4662248aa7a23e748b000975a87fa4a431f49ec632b
4
+ data.tar.gz: f3aa243431e0aab1d84d4cbc48ee6a329581acedc7a9c70e2a13e7bb0e15b529
5
5
  SHA512:
6
- metadata.gz: 4991adea5ee65493ea99abe94c19360fc6cc718048784431409abc08fbaf1b1efe3b304dedbd0994d8b66b38294b41ea6400bf5de1f03f09694723a7b709e77c
7
- data.tar.gz: 00f47a3efd2de6565dd5f5a3a64caa4b610e282d16620975376876501130a6d0cdbbef9412254b8f3d09db2df7a1f3f62d3a8e581c4fcfecd27ea3224cb6f20f
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017-2023 Andrew Kane
1
+ Copyright (c) 2017-2024 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
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
- [![Build Status](https://github.com/ankane/dexter/workflows/build/badge.svg?branch=master)](https://github.com/ankane/dexter/actions)
7
+ [![Build Status](https://github.com/ankane/dexter/actions/workflows/build.yml/badge.svg)](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.3.1.tar.gz | tar xz
16
- cd hypopg-1.3.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/app-framework-services/p/support-hypopg-for-postgres)
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
 
@@ -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.explainable? && q.high_cost? }
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
- find_columns(query.tree).each do |col|
201
- last_col = col["fields"].last
202
- if last_col["String"]
203
- possible_columns << last_col["String"]["sval"]
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
- conn.exec_params("#{query} /*dexter*/", params).to_a
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
- explain_normalized = query.include?("$1")
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
@@ -31,7 +31,7 @@ module Dexter
31
31
  end
32
32
 
33
33
  def explainable?
34
- plans.any?
34
+ plans.size >= 3
35
35
  end
36
36
 
37
37
  def costs
@@ -1,3 +1,3 @@
1
1
  module Dexter
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.2"
3
3
  end
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.0
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: 2023-04-18 00:00:00.000000000 Z
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.4.10
113
+ rubygems_version: 3.5.3
100
114
  signing_key:
101
115
  specification_version: 4
102
116
  summary: The automatic indexer for Postgres