pgdexter 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e51c5c9ce5c31760d773177a3b224674752b19de8cc9fa0978e1da2adff9a490
4
- data.tar.gz: ce9b2e89d2524aafda39bd5a91bb53abde5d0819e0c8751281a5ff7ad10766c2
3
+ metadata.gz: c797cdeb10797636a6406b017fc9276ea6522656f6592769b5088bee56165cfb
4
+ data.tar.gz: 76dc688fcea9ccde4aead259ef582f6103d62a5f40e7be668ee2b03347a14470
5
5
  SHA512:
6
- metadata.gz: 63cdf589a11ab232f45e26ff8b6a713ef757ce3bc3baf3fecbc51217fb4c3a4bdda0ded52bc631bbb8aaa5fe7ca28d356ab99e200c5af22a3bd1bac9e5efc17b
7
- data.tar.gz: fcbb680a98f5907a62216ee142fa91b8538965c1fd59c1de48645ee1b5e3525950245d010a128f52b28fda665d0f6fcbf95fa343184b559150646819049f173a
6
+ metadata.gz: a5ac6bf812b56648ac0b1f864baf025b7cd4e81cccf6d6de65f1780fd63f8f1e640aff53aca771cece0ce54267ad7a428aaba8f42773591380547fac05bfe94a
7
+ data.tar.gz: 7c0f8b36467cc5eacbc96f6e5e2132de3526ea9827cab9c5ede7e1eff2b471f22948e0b202f1a00fd8b37b785934eec8508ad561947e50efa9b9ffb07e5eb9ae
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.3.5
2
+
3
+ - Added `sql` input format
4
+ - Fixed error for queries with double dash comments
5
+ - Fixed connection threading issue with `--pg-stat-activity` option
6
+
1
7
  ## 0.3.4
2
8
 
3
9
  - Fixed `--username` option
data/README.md CHANGED
@@ -104,7 +104,7 @@ or pass files:
104
104
  dexter <connection-options> <file1> <file2>
105
105
  ```
106
106
 
107
- or collect running queries with: [master]
107
+ or collect running queries with:
108
108
 
109
109
  ```sh
110
110
  dexter <connection-options> --pg-stat-activity
@@ -215,6 +215,10 @@ gem specific_install https://github.com/ankane/dexter.git
215
215
 
216
216
  This software wouldn’t be possible without [HypoPG](https://github.com/dalibo/hypopg), which allows you to create hypothetical indexes, and [pg_query](https://github.com/lfittl/pg_query), which allows you to parse and fingerprint queries. A big thanks to Dalibo and Lukas Fittl respectively.
217
217
 
218
+ ## Research
219
+
220
+ This is known as the Index Selection Problem (ISP).
221
+
218
222
  ## Contributing
219
223
 
220
224
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
@@ -236,5 +240,6 @@ rake install
236
240
  To run tests, use:
237
241
 
238
242
  ```sh
243
+ createdb dexter_test
239
244
  rake test
240
245
  ```
data/lib/dexter.rb CHANGED
@@ -12,6 +12,7 @@ require "dexter/indexer"
12
12
  require "dexter/log_parser"
13
13
  require "dexter/csv_log_parser"
14
14
  require "dexter/pg_stat_activity_parser"
15
+ require "dexter/sql_log_parser"
15
16
  require "dexter/processor"
16
17
  require "dexter/query"
17
18
 
@@ -14,6 +14,7 @@ module Dexter
14
14
  @analyze = options[:analyze]
15
15
  @min_cost_savings_pct = options[:min_cost_savings_pct].to_i
16
16
  @options = options
17
+ @mutex = Mutex.new
17
18
 
18
19
  create_extension unless extension_exists?
19
20
  execute("SET lock_timeout = '5s'")
@@ -181,7 +182,7 @@ module Dexter
181
182
  query.plans << plan(query.statement)
182
183
  if @log_explain
183
184
  # Pass format to prevent ANALYZE
184
- puts execute("EXPLAIN (FORMAT TEXT) #{safe_statement(query.statement)}").map { |r| r["QUERY PLAN"] }.join("\n")
185
+ puts execute("EXPLAIN (FORMAT TEXT) #{safe_statement(query.statement)}", pretty: false).map { |r| r["QUERY PLAN"] }.join("\n")
185
186
  end
186
187
  rescue PG::Error, JSON::NestingError => e
187
188
  if @log_explain
@@ -362,11 +363,12 @@ module Dexter
362
363
  winning_cost < query.initial_cost * savings_ratio
363
364
  end
364
365
 
366
+ query_indexes = [winning_index]
367
+ new_cost3 = winning_cost
368
+ query.pass3_indexes = query_indexes
369
+
365
370
  if use_winning
366
- query_indexes = [winning_index]
367
371
  cost_savings3 = true
368
- new_cost3 = winning_cost
369
- query.pass3_indexes = query_indexes
370
372
  else
371
373
  suggest_index = false
372
374
  end
@@ -456,7 +458,7 @@ module Dexter
456
458
  log "Pass3: #{query.costs[3]} : #{log_indexes(query.pass3_indexes || [])}"
457
459
  end
458
460
  log "Final: #{query.new_cost} : #{log_indexes(query.suggest_index ? query_indexes : [])}"
459
- if query_indexes.size == 1 && !query.suggest_index
461
+ if (query.pass1_indexes.any? || query.pass2_indexes.any?) && !query.suggest_index
460
462
  log "Need #{@min_cost_savings_pct}% cost savings to suggest index"
461
463
  end
462
464
  else
@@ -516,7 +518,7 @@ module Dexter
516
518
  abort e.message
517
519
  end
518
520
 
519
- def execute(query)
521
+ def execute(query, pretty: true)
520
522
  # use exec_params instead of exec for security
521
523
  #
522
524
  # Unlike PQexec, PQexecParams allows at most one SQL command in the given string.
@@ -524,14 +526,17 @@ module Dexter
524
526
  # This is a limitation of the underlying protocol, but has some usefulness
525
527
  # as an extra defense against SQL-injection attacks.
526
528
  # https://www.postgresql.org/docs/current/static/libpq-exec.html
527
- query = squish(query)
529
+ query = squish(query) if pretty
528
530
  log "SQL: #{query}" if @log_sql
529
- conn.exec_params(query, []).to_a
531
+
532
+ @mutex.synchronize do
533
+ conn.exec_params(query, []).to_a
534
+ end
530
535
  end
531
536
 
532
537
  def plan(query)
533
538
  # strip semi-colons as another measure of defense
534
- JSON.parse(execute("EXPLAIN (FORMAT JSON) #{safe_statement(query)}").first["QUERY PLAN"], max_nesting: 1000).first["Plan"]
539
+ JSON.parse(execute("EXPLAIN (FORMAT JSON) #{safe_statement(query)}", pretty: false).first["QUERY PLAN"], max_nesting: 1000).first["Plan"]
535
540
  end
536
541
 
537
542
  # TODO for multicolumn indexes, use ordering
@@ -18,7 +18,7 @@ module Dexter
18
18
 
19
19
  queries = new_queries
20
20
 
21
- sleep(5)
21
+ sleep(1)
22
22
  end
23
23
  end
24
24
  end
@@ -13,6 +13,8 @@ module Dexter
13
13
  PgStatActivityParser.new(@indexer, @collector)
14
14
  elsif options[:input_format] == "csv"
15
15
  CsvLogParser.new(logfile, @collector)
16
+ elsif options[:input_format] == "sql"
17
+ SqlLogParser.new(logfile, @collector)
16
18
  else
17
19
  LogParser.new(logfile, @collector)
18
20
  end
@@ -0,0 +1,10 @@
1
+ module Dexter
2
+ class SqlLogParser < LogParser
3
+ def perform
4
+ # TODO support streaming
5
+ @logfile.read.split(";").each do |statement|
6
+ process_entry(statement, 1)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -1,3 +1,3 @@
1
1
  module Dexter
2
- VERSION = "0.3.4"
2
+ VERSION = "0.3.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgdexter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-10 00:00:00.000000000 Z
11
+ date: 2018-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slop
@@ -122,6 +122,7 @@ files:
122
122
  - lib/dexter/pg_stat_activity_parser.rb
123
123
  - lib/dexter/processor.rb
124
124
  - lib/dexter/query.rb
125
+ - lib/dexter/sql_log_parser.rb
125
126
  - lib/dexter/version.rb
126
127
  - pgdexter.gemspec
127
128
  homepage: https://github.com/ankane/dexter