pgdexter 0.6.0 → 0.6.1

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: 234a20833730445c80e03b5d789f174f9f50e9fb443614938912aaa1f74a7bd8
4
- data.tar.gz: b286dd2c8735f17cd943c71405a2863699d4540487f5f1c730f5c19e6337da79
3
+ metadata.gz: 5d6f485cb3dbfbc0e20b1b46b2ac799c27664b2130dbff0c57a4451d9df4fd3f
4
+ data.tar.gz: 76d48363c209d2b17a0dd29dd8e8be9e691b3b161f004e016d533871a21da6ce
5
5
  SHA512:
6
- metadata.gz: 6464fe3a0d24567a1759272ca8ab82b6462c88c5221f10a7784a0fdf41234268896b0368fbd20e442ce8f7a307d917c6779f484006c1c4f7971be07a8ba26de1
7
- data.tar.gz: e3b0e7abc058f8f250e431d8b332e761a83bbc737b782449804a663f251dd7f33dc3213cc247dfb6be06b636c1d166af54803c13d7541dd9a8479baecaa86e8c
6
+ metadata.gz: ac7db9aa636b187745bdaba28c84581d2610397368eceb0c86173c55ea88733abdc9876280bac5cf4f940141510d1e37c75b5cb967de00c0b2cb434eaccee121
7
+ data.tar.gz: bebeb10a06631cd3ba5a69d40a67b3275d7d2dd8329ab617f3251327ab2f2980d0bce273d0c44ff1e31fb6992e04220524c22e3eaa15eaca674987cb2074024a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.6.1 (2025-06-08)
2
+
3
+ - Fixed error with column types without `btree` support
4
+
1
5
  ## 0.6.0 (2025-06-01)
2
6
 
3
7
  - Added Linux packages for Ubuntu 24.04 and Debian 12
data/README.md CHANGED
@@ -232,10 +232,10 @@ If compilation fails with `fatal error: postgres.h: No such file or directory`,
232
232
  For Ubuntu and Debian, use:
233
233
 
234
234
  ```sh
235
- sudo apt-get install postgresql-server-dev-16
235
+ sudo apt-get install postgresql-server-dev-17
236
236
  ```
237
237
 
238
- Note: Replace `16` with your Postgres server version
238
+ Note: Replace `17` with your Postgres server version
239
239
 
240
240
  ## Additional Installation Methods
241
241
 
data/lib/dexter/client.rb CHANGED
@@ -112,7 +112,7 @@ module Dexter
112
112
 
113
113
  options[:dbname] = arguments.shift unless options[:dbname]
114
114
 
115
- # TODO don't use global var
115
+ # TODO remove global variable
116
116
  $log_level = options[:log_level].to_s.downcase
117
117
  unless ["error", "info", "debug", "debug2", "debug3"].include?($log_level)
118
118
  raise Error, "Unknown log level"
@@ -41,6 +41,10 @@ module Dexter
41
41
  end
42
42
  end
43
43
 
44
+ def quote_ident(value)
45
+ value.split(".").map { |v| conn.quote_ident(v) }.join(".")
46
+ end
47
+
44
48
  def server_version_num
45
49
  @server_version_num ||= execute("SHOW server_version_num").first["server_version_num"].to_i
46
50
  end
@@ -0,0 +1,72 @@
1
+ module Dexter
2
+ class IndexCreator
3
+ include Logging
4
+
5
+ def initialize(connection, indexer, new_indexes, tablespace)
6
+ @connection = connection
7
+ @indexer = indexer
8
+ @new_indexes = new_indexes
9
+ @tablespace = tablespace
10
+ end
11
+
12
+ # 1. create lock
13
+ # 2. refresh existing index list
14
+ # 3. create indexes that still don't exist
15
+ # 4. release lock
16
+ def perform
17
+ with_advisory_lock do
18
+ @new_indexes.each do |index|
19
+ unless index_exists?(index)
20
+ statement = String.new("CREATE INDEX CONCURRENTLY ON #{@connection.quote_ident(index[:table])} (#{index[:columns].map { |c| @connection.quote_ident(c) }.join(", ")})")
21
+ statement << " TABLESPACE #{@connection.quote_ident(@tablespace)}" if @tablespace
22
+ log "Creating index: #{statement}"
23
+ started_at = monotonic_time
24
+ begin
25
+ @connection.execute(statement)
26
+ log "Index created: #{((monotonic_time - started_at) * 1000).to_i} ms"
27
+ rescue PG::LockNotAvailable
28
+ log "Could not acquire lock: #{index[:table]}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def monotonic_time
38
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
39
+ end
40
+
41
+ def with_advisory_lock
42
+ lock_id = 123456
43
+ first_time = true
44
+ while @connection.execute("SELECT pg_try_advisory_lock($1)", params: [lock_id]).first["pg_try_advisory_lock"] != "t"
45
+ if first_time
46
+ log "Waiting for lock..."
47
+ first_time = false
48
+ end
49
+ sleep(1)
50
+ end
51
+ yield
52
+ ensure
53
+ suppress_messages do
54
+ @connection.execute("SELECT pg_advisory_unlock($1)", params: [lock_id])
55
+ end
56
+ end
57
+
58
+ def suppress_messages
59
+ @connection.send(:conn).set_notice_processor do |message|
60
+ # do nothing
61
+ end
62
+ yield
63
+ ensure
64
+ # clear notice processor
65
+ @connection.send(:conn).set_notice_processor
66
+ end
67
+
68
+ def index_exists?(index)
69
+ @indexer.send(:indexes, [index[:table]]).find { |i| i["columns"] == index[:columns] }
70
+ end
71
+ end
72
+ end
@@ -30,6 +30,7 @@ module Dexter
30
30
 
31
31
  if tables.any?
32
32
  # analyze tables if needed
33
+ # TODO remove @log_level in 0.7.0
33
34
  analyze_tables(tables) if @analyze || @log_level == "debug2"
34
35
 
35
36
  # get initial costs for queries
@@ -41,7 +42,8 @@ module Dexter
41
42
  ColumnResolver.new(@connection, candidate_queries, log_level: @log_level).perform
42
43
  candidate_queries.each do |query|
43
44
  # no reason to use btree index for json columns
44
- query.candidate_columns = query.columns.reject { |c| ["json", "jsonb"].include?(c[:type]) }.sort_by { |c| [c[:table], c[:column]] }
45
+ # TODO check type supports btree
46
+ query.candidate_columns = query.columns.reject { |c| ["json", "jsonb", "point"].include?(c[:type]) }.sort_by { |c| [c[:table], c[:column]] }
45
47
  end
46
48
  candidate_queries.select! { |q| q.candidate_columns.any? }
47
49
 
@@ -59,7 +61,7 @@ module Dexter
59
61
  show_debug_info(new_indexes, queries) if @log_level.start_with?("debug")
60
62
 
61
63
  # create new indexes
62
- create_indexes(new_indexes) if @create && new_indexes.any?
64
+ IndexCreator.new(@connection, self, new_indexes, @tablespace).perform if @create && new_indexes.any?
63
65
  end
64
66
 
65
67
  private
@@ -120,7 +122,7 @@ module Dexter
120
122
  end
121
123
 
122
124
  if @analyze && (!la || la < Time.now - 3600)
123
- statement = "ANALYZE #{quote_ident(table)}"
125
+ statement = "ANALYZE #{@connection.quote_ident(table)}"
124
126
  log "Running analyze: #{statement}"
125
127
  execute(statement)
126
128
  end
@@ -183,8 +185,12 @@ module Dexter
183
185
 
184
186
  def create_candidate_indexes(candidate_indexes, index_mapping)
185
187
  candidate_indexes.each do |columns|
186
- index_name = create_hypothetical_index(columns[0][:table], columns.map { |c| c[:column] })
187
- index_mapping[index_name] = columns
188
+ begin
189
+ index_name = create_hypothetical_index(columns[0][:table], columns.map { |c| c[:column] })
190
+ index_mapping[index_name] = columns
191
+ rescue PG::UndefinedObject
192
+ # data type x has no default operator class for access method "btree"
193
+ end
188
194
  end
189
195
  rescue PG::InternalError
190
196
  # hypopg: not more oid available
@@ -446,37 +452,6 @@ module Dexter
446
452
  end
447
453
  end
448
454
 
449
- # 1. create lock
450
- # 2. refresh existing index list
451
- # 3. create indexes that still don't exist
452
- # 4. release lock
453
- def create_indexes(new_indexes)
454
- with_advisory_lock do
455
- new_indexes.each do |index|
456
- unless index_exists?(index)
457
- statement = String.new("CREATE INDEX CONCURRENTLY ON #{quote_ident(index[:table])} (#{index[:columns].map { |c| quote_ident(c) }.join(", ")})")
458
- statement << " TABLESPACE #{quote_ident(@tablespace)}" if @tablespace
459
- log "Creating index: #{statement}"
460
- started_at = monotonic_time
461
- begin
462
- execute(statement)
463
- log "Index created: #{((monotonic_time - started_at) * 1000).to_i} ms"
464
- rescue PG::LockNotAvailable
465
- log "Could not acquire lock: #{index[:table]}"
466
- end
467
- end
468
- end
469
- end
470
- end
471
-
472
- def monotonic_time
473
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
474
- end
475
-
476
- def conn
477
- @connection.send(:conn)
478
- end
479
-
480
455
  def execute(...)
481
456
  @connection.execute(...)
482
457
  end
@@ -522,38 +497,7 @@ module Dexter
522
497
  end
523
498
 
524
499
  def create_hypothetical_index(table, columns)
525
- execute("SELECT * FROM hypopg_create_index('CREATE INDEX ON #{quote_ident(table)} (#{columns.map { |c| quote_ident(c) }.join(", ")})')").first["indexname"]
526
- end
527
-
528
- def with_advisory_lock
529
- lock_id = 123456
530
- first_time = true
531
- while execute("SELECT pg_try_advisory_lock($1)", params: [lock_id]).first["pg_try_advisory_lock"] != "t"
532
- if first_time
533
- log "Waiting for lock..."
534
- first_time = false
535
- end
536
- sleep(1)
537
- end
538
- yield
539
- ensure
540
- suppress_messages do
541
- execute("SELECT pg_advisory_unlock($1)", params: [lock_id])
542
- end
543
- end
544
-
545
- def suppress_messages
546
- conn.set_notice_processor do |message|
547
- # do nothing
548
- end
549
- yield
550
- ensure
551
- # clear notice processor
552
- conn.set_notice_processor
553
- end
554
-
555
- def index_exists?(index)
556
- indexes([index[:table]]).find { |i| i["columns"] == index[:columns] }
500
+ execute("SELECT * FROM hypopg_create_index('CREATE INDEX ON #{@connection.quote_ident(table)} (#{columns.map { |c| @connection.quote_ident(c) }.join(", ")})')").first["indexname"]
557
501
  end
558
502
 
559
503
  def indexes(tables)
@@ -590,10 +534,6 @@ module Dexter
590
534
  end
591
535
  end
592
536
 
593
- def quote_ident(value)
594
- value.split(".").map { |v| conn.quote_ident(v) }.join(".")
595
- end
596
-
597
537
  def safe_statement(statement)
598
538
  statement.gsub(";", "")
599
539
  end
@@ -8,7 +8,7 @@ module Dexter
8
8
  }
9
9
 
10
10
  def output
11
- $stdout
11
+ $dexter_output || $stdout
12
12
  end
13
13
 
14
14
  def log(message = "")
@@ -1,3 +1,3 @@
1
1
  module Dexter
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.1"
3
3
  end
data/lib/dexter.rb CHANGED
@@ -15,6 +15,7 @@ require_relative "dexter/client"
15
15
  require_relative "dexter/collector"
16
16
  require_relative "dexter/column_resolver"
17
17
  require_relative "dexter/connection"
18
+ require_relative "dexter/index_creator"
18
19
  require_relative "dexter/indexer"
19
20
  require_relative "dexter/processor"
20
21
  require_relative "dexter/query"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgdexter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
@@ -80,6 +80,7 @@ files:
80
80
  - lib/dexter/collector.rb
81
81
  - lib/dexter/column_resolver.rb
82
82
  - lib/dexter/connection.rb
83
+ - lib/dexter/index_creator.rb
83
84
  - lib/dexter/indexer.rb
84
85
  - lib/dexter/logging.rb
85
86
  - lib/dexter/parsers/csv_log_parser.rb