ruby-pg-extras 4.5.1 → 4.6.0

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: d029c96b61595f277b52f37a747a644943f7569394a9d7bf3e84758397e40174
4
- data.tar.gz: 0f5abfe2d07b9030d0eb768d6e3712d139eecafa9c12d77c4ac51f802bc9f007
3
+ metadata.gz: 250bb1e08a0937724fc2cf90c16d26670218a1c23884fd69f33f7379bb550a20
4
+ data.tar.gz: 12e69c803e3bd48287445bd46451255b3a0074fb3c1d4a0d373bb2d64a98e900
5
5
  SHA512:
6
- metadata.gz: '083ed61d5ab3804b90416da21b11e4463efda2e2e81dbd457b60bbb31bbb165ef2e097d1eb65114437a87e54c6aa434e214630c3652f47871a3ac40b3bb4eb88'
7
- data.tar.gz: 8451042b71129d99672ad90d6906c31bee8181ba4e0acc12af68b85ab14a9f5eac19e2ecea7dd485fc7b7738b02f2d8df3c8d74b26e976f087cd906ac1b9ac2d
6
+ metadata.gz: b68d9e6c988df34cb7e3236ce4c12a7dbfdef23139dbc61cfe6364e21f95b55f0ca11c6f2657e1d350cf16fbb641a72f91228a12569b785061ee4e0d65737e45
7
+ data.tar.gz: d31f3679f3ffd66bc9a39e9868985a260807a32af5335e722ccba5f5df515c80d451b957544fcaa7e016e2bbbebe09992cd89a7046cb033639a48eb95bb43fbe
data/.circleci/config.yml CHANGED
@@ -2,30 +2,37 @@ version: 2
2
2
  jobs:
3
3
  test:
4
4
  docker:
5
- - image: circleci/ruby:2.6.5
5
+ - image: cimg/ruby:2.6
6
6
  environment:
7
7
  DATABASE_URL: postgresql://postgres:secret@localhost:5432/ruby-pg-extras-test
8
- - image: circleci/postgres:11.5
8
+ - image: cimg/postgres:11.15
9
9
  command: postgres -c shared_preload_libraries=pg_stat_statements
10
10
  name: postgres11
11
11
  environment:
12
12
  POSTGRES_USER: postgres
13
13
  POSTGRES_DB: ruby-pg-extras-test
14
14
  POSTGRES_PASSWORD: secret
15
- - image: circleci/postgres:12.7
15
+ - image: cimg/postgres:12.10
16
16
  command: postgres -c shared_preload_libraries=pg_stat_statements
17
17
  name: postgres12
18
18
  environment:
19
19
  POSTGRES_USER: postgres
20
20
  POSTGRES_DB: ruby-pg-extras-test
21
21
  POSTGRES_PASSWORD: secret
22
- - image: circleci/postgres:13.3
22
+ - image: cimg/postgres:13.6
23
23
  command: postgres -c shared_preload_libraries=pg_stat_statements
24
24
  name: postgres13
25
25
  environment:
26
26
  POSTGRES_USER: postgres
27
27
  POSTGRES_DB: ruby-pg-extras-test
28
28
  POSTGRES_PASSWORD: secret
29
+ - image: cimg/postgres:14.2
30
+ command: postgres -c shared_preload_libraries=pg_stat_statements
31
+ name: postgres14
32
+ environment:
33
+ POSTGRES_USER: postgres
34
+ POSTGRES_DB: ruby-pg-extras-test
35
+ POSTGRES_PASSWORD: secret
29
36
  parallelism: 1
30
37
  steps:
31
38
  - checkout
@@ -51,6 +58,11 @@ jobs:
51
58
  environment:
52
59
  DATABASE_URL: postgresql://postgres:secret@postgres13:5432/ruby-pg-extras-test
53
60
  command: bundle exec rspec spec/
61
+ - run:
62
+ name: Run specs for PG 14
63
+ environment:
64
+ DATABASE_URL: postgresql://postgres:secret@postgres14:5432/ruby-pg-extras-test
65
+ command: bundle exec rspec spec/
54
66
  workflows:
55
67
  version: 2
56
68
  test:
data/Rakefile CHANGED
@@ -5,5 +5,5 @@ RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  desc 'Test all PG versions'
7
7
  task :test_all do
8
- system("PG_VERSION=11 bundle exec rspec spec/ && PG_VERSION=12 bundle exec rspec spec/ && PG_VERSION=13 bundle exec rspec spec/")
8
+ system("PG_VERSION=11 bundle exec rspec spec/ && PG_VERSION=12 bundle exec rspec spec/ && PG_VERSION=13 bundle exec rspec spec/ && PG_VERSION=14 bundle exec rspec spec/")
9
9
  end
@@ -2,7 +2,7 @@ version: '3'
2
2
 
3
3
  services:
4
4
  postgres11:
5
- image: postgres:11.5-alpine
5
+ image: postgres:11.16-alpine
6
6
  command: postgres -c shared_preload_libraries=pg_stat_statements
7
7
  environment:
8
8
  POSTGRES_USER: postgres
@@ -11,7 +11,7 @@ services:
11
11
  ports:
12
12
  - '5432:5432'
13
13
  postgres12:
14
- image: postgres:12.7-alpine
14
+ image: postgres:12.11-alpine
15
15
  command: postgres -c shared_preload_libraries=pg_stat_statements
16
16
  environment:
17
17
  POSTGRES_USER: postgres
@@ -20,7 +20,7 @@ services:
20
20
  ports:
21
21
  - '5433:5432'
22
22
  postgres13:
23
- image: postgres:13.3-alpine
23
+ image: postgres:13.7-alpine
24
24
  command: postgres -c shared_preload_libraries=pg_stat_statements
25
25
  environment:
26
26
  POSTGRES_USER: postgres
@@ -28,3 +28,12 @@ services:
28
28
  POSTGRES_PASSWORD: secret
29
29
  ports:
30
30
  - '5434:5432'
31
+ postgres14:
32
+ image: postgres:14.3-alpine
33
+ command: postgres -c shared_preload_libraries=pg_stat_statements
34
+ environment:
35
+ POSTGRES_USER: postgres
36
+ POSTGRES_DB: ruby-pg-extras-test
37
+ POSTGRES_PASSWORD: secret
38
+ ports:
39
+ - '5435:5432'
@@ -3,6 +3,7 @@
3
3
  require 'terminal-table'
4
4
  require 'uri'
5
5
  require 'pg'
6
+ require 'ruby_pg_extras/size_parser'
6
7
  require 'ruby_pg_extras/diagnose_data'
7
8
  require 'ruby_pg_extras/diagnose_print'
8
9
  require 'ruby_pg_extras/index_info'
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'filesize'
4
-
5
3
  module RubyPgExtras
6
4
  class DiagnoseData
7
5
  PG_EXTRAS_TABLE_CACHE_HIT_MIN_EXPECTED = "0.985"
8
6
  PG_EXTRAS_INDEX_CACHE_HIT_MIN_EXPECTED = "0.985"
9
7
  PG_EXTRAS_UNUSED_INDEXES_MAX_SCANS = 20
10
- PG_EXTRAS_UNUSED_INDEXES_MIN_SIZE_BYTES = Filesize.from("1 MB").to_i # 1000000 bytes
8
+ PG_EXTRAS_UNUSED_INDEXES_MIN_SIZE_BYTES = SizeParser.to_i("1 MB") # 1000000 bytes
11
9
  PG_EXTRAS_NULL_INDEXES_MIN_SIZE_MB = 1 # 1 MB
12
10
  PG_EXTRAS_NULL_MIN_NULL_FRAC_PERCENT = 50 # 50%
13
11
  PG_EXTRAS_BLOAT_MIN_VALUE = 10
@@ -118,7 +116,7 @@ module RubyPgExtras
118
116
  in_format: :hash,
119
117
  args: { min_scans: PG_EXTRAS_UNUSED_INDEXES_MAX_SCANS }
120
118
  ).select do |i|
121
- Filesize.from(i.fetch("index_size").sub("bytes", "").strip).to_i >= PG_EXTRAS_UNUSED_INDEXES_MIN_SIZE_BYTES
119
+ SizeParser.to_i(i.fetch("index_size").strip) >= PG_EXTRAS_UNUSED_INDEXES_MIN_SIZE_BYTES
122
120
  end
123
121
 
124
122
  if indexes.count == 0
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPgExtras
4
+ class SizeParser
5
+ def self.to_i(arg)
6
+ value = to_i_si(arg) || to_i_binary(arg) || to_i_digits(arg)
7
+ raise ArgumentError, "Unparseable size: #{arg}" if value.nil?
8
+
9
+ value
10
+ end
11
+
12
+ def self.regexp_for_units(units)
13
+ /\A(-?\d+)\s?(#{units.join('|')})\z/i
14
+ end
15
+
16
+ SI_UNITS = %w[bytes kB MB GB TB PB EB ZB YB].map(&:downcase).freeze
17
+ SI_REGEXP = regexp_for_units(SI_UNITS)
18
+
19
+ def self.to_i_si(arg)
20
+ to_i_for_units(arg, SI_REGEXP, SI_UNITS, 1000)
21
+ end
22
+
23
+ BINARY_UNITS = %w[bytes KiB MiB GiB TiB PiB EiB ZiB YiB].map(&:downcase).freeze
24
+ BINARY_REGEXP = regexp_for_units(BINARY_UNITS)
25
+
26
+ def self.to_i_binary(arg)
27
+ to_i_for_units(arg, BINARY_REGEXP, BINARY_UNITS, 1024)
28
+ end
29
+
30
+ def self.to_i_for_units(arg, regexp, units, multiplier)
31
+ match_data = regexp.match(arg)
32
+ return nil unless match_data
33
+
34
+ exponent = units.index(match_data[2].downcase).to_i
35
+ match_data[1].to_i * multiplier**exponent
36
+ end
37
+
38
+ DIGITS_ONLY_REGEXP = /\A(-?\d+)\z/.freeze
39
+ def self.to_i_digits(arg)
40
+ DIGITS_ONLY_REGEXP.match?(arg) ? arg.to_i : nil
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyPgExtras
4
- VERSION = "4.5.1"
4
+ VERSION = "4.6.0"
5
5
  end
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.require_paths = ["lib"]
17
17
  s.license = "MIT"
18
18
  s.add_dependency "pg"
19
- s.add_dependency "filesize"
20
19
  s.add_dependency "terminal-table"
21
20
  s.add_development_dependency "rake"
22
21
  s.add_development_dependency "rspec"
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RubyPgExtras::SizeParser do
6
+ subject(:result) { described_class.to_i(arg) }
7
+
8
+ describe 'SI Units' do
9
+ let(:arg) { "#{num_units} #{unit}" }
10
+
11
+ context "when the argument is a number followed by 'bytes', with possible case variations" do
12
+ let(:num_units) { rand(1000) }
13
+ let(:unit) { %w[bytes BYTES Bytes].sample }
14
+
15
+ it { is_expected.to eq(num_units) }
16
+ end
17
+
18
+ context "when the argument is a number followed by 'kB', with possible case variations" do
19
+ let(:num_units) { rand(1000) }
20
+ let(:unit) { %w[kB kb KB].sample }
21
+
22
+ it { is_expected.to eq(num_units * 1000) }
23
+ end
24
+
25
+ context "when the argument is a number followed by 'MB', with possible case variations" do
26
+ let(:num_units) { rand(1000) }
27
+ let(:unit) { %w[MB Mb mb].sample }
28
+
29
+ it { is_expected.to eq(num_units * 1000 * 1000) }
30
+ end
31
+
32
+ context "when the argument is a number followed by 'GB', with possible case variations" do
33
+ let(:num_units) { rand(1000) }
34
+ let(:unit) { %w[GB Gb gb].sample }
35
+
36
+ it { is_expected.to eq(num_units * 1000 * 1000 * 1000) }
37
+ end
38
+
39
+ context "when the argument is a number followed by 'TB', with possible case variations" do
40
+ let(:num_units) { rand(1000) }
41
+ let(:unit) { %w[TB Tb tb].sample }
42
+
43
+ it { is_expected.to eq(num_units * 1000 * 1000 * 1000 * 1000) }
44
+ end
45
+
46
+ context "when the argument is a number followed by 'PB', with possible case variations" do
47
+ let(:num_units) { rand(1000) }
48
+ let(:unit) { %w[PB Pb pb].sample }
49
+
50
+ it { is_expected.to eq(num_units * 1000 * 1000 * 1000 * 1000 * 1000) }
51
+ end
52
+
53
+ context "when the argument is a number followed by 'EB', with possible case variations" do
54
+ let(:num_units) { rand(1000) }
55
+ let(:unit) { %w[EB Eb eb].sample }
56
+
57
+ it { is_expected.to eq(num_units * 1000 * 1000 * 1000 * 1000 * 1000 * 1000) }
58
+ end
59
+
60
+ context "when the argument is a number followed by 'ZB', with possible case variations" do
61
+ let(:num_units) { 912 }
62
+ let(:unit) { %w[ZB Zb zb].sample }
63
+
64
+ it { is_expected.to eq(num_units * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000) }
65
+ end
66
+
67
+ context "when the argument is a number followed by 'YB', with possible case variations" do
68
+ let(:num_units) { 912 }
69
+ let(:unit) { %w[YB Yb yb].sample }
70
+
71
+ it { is_expected.to eq(num_units * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000) }
72
+ end
73
+ end
74
+
75
+ describe 'Binary Units' do
76
+ let(:arg) { "#{num_units} #{unit}" }
77
+
78
+ context "when the argument is a number followed by 'bytes', with possible case variations" do
79
+ let(:num_units) { rand(1000) }
80
+ let(:unit) { %w[bytes BYTES Bytes].sample }
81
+
82
+ it { is_expected.to eq(num_units) }
83
+ end
84
+
85
+ context "when the argument is a number followed by 'kiB', with possible case variations" do
86
+ let(:num_units) { rand(1000) }
87
+ let(:unit) { %w[kiB kib KiB].sample }
88
+
89
+ it { is_expected.to eq(num_units * 1024) }
90
+ end
91
+
92
+ context "when the argument is a number followed by 'MiB', with possible case variations" do
93
+ let(:num_units) { rand(1000) }
94
+ let(:unit) { %w[MiB Mib mib].sample }
95
+
96
+ it { is_expected.to eq(num_units * 1024 * 1024) }
97
+ end
98
+
99
+ context "when the argument is a number followed by 'GiB', with possible case variations" do
100
+ let(:num_units) { rand(1000) }
101
+ let(:unit) { %w[GiB Gib gib].sample }
102
+
103
+ it { is_expected.to eq(num_units * 1024 * 1024 * 1024) }
104
+ end
105
+
106
+ context "when the argument is a number followed by 'TiB', with possible case variations" do
107
+ let(:num_units) { rand(1000) }
108
+ let(:unit) { %w[TiB Tib tib].sample }
109
+
110
+ it { is_expected.to eq(num_units * 1024 * 1024 * 1024 * 1024) }
111
+ end
112
+
113
+ context "when the argument is a number followed by 'PiB', with possible case variations" do
114
+ let(:num_units) { rand(1000) }
115
+ let(:unit) { %w[PiB Pib pib].sample }
116
+
117
+ it { is_expected.to eq(num_units * 1024 * 1024 * 1024 * 1024 * 1024) }
118
+ end
119
+
120
+ context "when the argument is a number followed by 'EiB', with possible case variations" do
121
+ let(:num_units) { rand(1000) }
122
+ let(:unit) { %w[EiB Eib eib].sample }
123
+
124
+ it { is_expected.to eq(num_units * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) }
125
+ end
126
+
127
+ context "when the argument is a number followed by 'ZB', with possible case variations" do
128
+ let(:num_units) { 912 }
129
+ let(:unit) { %w[ZiB Zib zib].sample }
130
+
131
+ it { is_expected.to eq(num_units * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) }
132
+ end
133
+
134
+ context "when the argument is a number followed by 'YB', with possible case variations" do
135
+ let(:num_units) { 912 }
136
+ let(:unit) { %w[YiB Yib yib].sample }
137
+
138
+ it { is_expected.to eq(num_units * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) }
139
+ end
140
+ end
141
+
142
+ context 'when the argument has only digits' do
143
+ let(:arg) { '654245' }
144
+
145
+ it { is_expected.to eq(arg.to_i) }
146
+ end
147
+
148
+ describe 'errors' do
149
+ it 'raises an error when the argument has an invalid prefix' do
150
+ expect do
151
+ described_class.to_i('123 qb')
152
+ end.to raise_error ArgumentError
153
+ end
154
+
155
+ it 'raises an error when the argument does not have a unit in bytes' do
156
+ expect do
157
+ described_class.to_i('123 mL')
158
+ end.to raise_error ArgumentError
159
+ end
160
+
161
+ it 'when the argument cannot be parsed an number of units' do
162
+ expect do
163
+ described_class.to_i('1c3 MB')
164
+ end.to raise_error ArgumentError
165
+ end
166
+ end
167
+ end
data/spec/spec_helper.rb CHANGED
@@ -12,6 +12,8 @@ elsif pg_version == "12"
12
12
  "5433"
13
13
  elsif pg_version == "13"
14
14
  "5434"
15
+ elsif pg_version == "14"
16
+ "5435"
15
17
  else
16
18
  "5432"
17
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-pg-extras
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.5.1
4
+ version: 4.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-13 00:00:00.000000000 Z
11
+ date: 2022-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: filesize
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: terminal-table
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -138,6 +124,7 @@ files:
138
124
  - lib/ruby_pg_extras/queries/total_table_size.sql
139
125
  - lib/ruby_pg_extras/queries/unused_indexes.sql
140
126
  - lib/ruby_pg_extras/queries/vacuum_stats.sql
127
+ - lib/ruby_pg_extras/size_parser.rb
141
128
  - lib/ruby_pg_extras/table_info.rb
142
129
  - lib/ruby_pg_extras/table_info_print.rb
143
130
  - lib/ruby_pg_extras/version.rb
@@ -146,6 +133,7 @@ files:
146
133
  - spec/diagnose_data_spec.rb
147
134
  - spec/diagnose_print_spec.rb
148
135
  - spec/index_info_spec.rb
136
+ - spec/size_parser_spec.rb
149
137
  - spec/smoke_spec.rb
150
138
  - spec/spec_helper.rb
151
139
  - spec/table_info_spec.rb
@@ -177,6 +165,7 @@ test_files:
177
165
  - spec/diagnose_data_spec.rb
178
166
  - spec/diagnose_print_spec.rb
179
167
  - spec/index_info_spec.rb
168
+ - spec/size_parser_spec.rb
180
169
  - spec/smoke_spec.rb
181
170
  - spec/spec_helper.rb
182
171
  - spec/table_info_spec.rb