schema_reaper 1.0.11 → 1.0.12

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: 5039780ddae77f5851cbc16afb53bea80df2eb7328253e18b89c2495feb66218
4
- data.tar.gz: e88e86f0cb4d723256505d9a4d7e89010c7cc532df86b57a1190713d71339a90
3
+ metadata.gz: b7e70e2066753282ad6757f3d73175a57b73b7026aa8879314f693dafc8547e7
4
+ data.tar.gz: d4b2ac7a4e6934e068d79ac32f694a5bffcd329bed689c23d0a080266b8bdb6b
5
5
  SHA512:
6
- metadata.gz: c4b095e2cc73c98f8415136810c3c00218b741cce186d3ff1d2d4e0063cd1658963aa60d296595bc4a2adc8dffaee982be49b750a8f7280993cc4e68a85fa4e0
7
- data.tar.gz: d7437c7dbeb2e0c96f4671c89b0e07e89e0e327bdd741cf6b6ee9c128eedf8c2bf96f58c3d925efe742b94de4632c6204ff363e5a23ddc1d6130aa38eeace44c
6
+ metadata.gz: '04943ff8915c5a809a9f81741f14e98712a6453dcf046441b7ae10d726031a994045e9490b5a8a62f948210c0e030825429bde387f35ffc15176a6b419e5f57a'
7
+ data.tar.gz: da080808761005f66096458968fc7c68d3c6f3251d2c97aa50a56020a37abb0d673129782244e880473ffb6287df191418f1142e1bc7632e4da05f49def17cc2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.12] - 2026-09-15
4
+
5
+ ### Fixed
6
+ - **`--format markdown` claimed `0 B reclaimable` when the row count was
7
+ unknown.** `Table` learned in 1.0.11 that `reclaimable_bytes` is `nil`
8
+ both when the true value is zero and when the row count has never been
9
+ measured, and that a report should say which. `Markdown` summed the raw
10
+ bytes directly and always printed a total, so on an unanalysed database
11
+ it stated there was nothing to reclaim — the exact claim the 1.0.11 fix
12
+ was written to stop, just in the one format meant for an unattended PR
13
+ comment or CI job summary. The per-row `Reclaims` column had the same
14
+ bug: an unmeasured finding showed `0.0 B`, indistinguishable from one
15
+ that genuinely frees nothing. The summary/unmeasured logic now lives in
16
+ one shared `Reporters::Reclaim` module used by both reporters, so the
17
+ two formats cannot answer the same question differently again. (#7,
18
+ mitkush)
19
+
3
20
  ## [1.0.11] - 2026-09-15
4
21
 
5
22
  Everything below landed after 1.0.10 was cut, so 1.0.10 on RubyGems contains
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "bytes"
3
+ require_relative "reclaim"
4
4
 
5
5
  module SchemaReaper
6
6
  module Reporters
@@ -18,8 +18,7 @@ module SchemaReaper
18
18
  return
19
19
  end
20
20
 
21
- @io.puts "\n#{@findings.size} finding(s), " \
22
- "~**#{Bytes.human(total)}** reclaimable.\n\n"
21
+ @io.puts "\n#{summary_line}\n\n"
23
22
  @io.puts "| Severity | Confidence | Type | Target | Reclaims | Fix |"
24
23
  @io.puts "|---|---|---|---|---|---|"
25
24
  rows.each { |r| @io.puts r }
@@ -27,17 +26,23 @@ module SchemaReaper
27
26
 
28
27
  private
29
28
 
29
+ # "54 finding(s). **~93.8 KB reclaimable.**" -- or, when the row count is
30
+ # unknown or the estimate is genuinely zero, no reclaim clause at all,
31
+ # matching the terminal report rather than claiming a number it does not
32
+ # have.
33
+ def summary_line
34
+ text = Reclaim.summary(@findings)
35
+ line = "#{@findings.size} finding(s)."
36
+ line + (text ? " **#{text}.**" : "")
37
+ end
38
+
30
39
  def rows
31
40
  @findings.sort_by { |f| -f.confidence }.map do |f|
32
41
  target = [f.table, f.column, f.index].compact.join("`.`")
33
42
  "| #{f.severity} | #{(f.confidence * 100).round}% | `#{f.type}` | " \
34
- "`#{target}` | #{Bytes.human(f.reclaimable_bytes)} | #{f.suggested_fix} |"
43
+ "`#{target}` | #{Reclaim.cell(f)} | #{f.suggested_fix} |"
35
44
  end
36
45
  end
37
-
38
- def total
39
- @findings.sum(&:reclaimable_bytes)
40
- end
41
46
  end
42
47
  end
43
48
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "bytes"
4
+
5
+ module SchemaReaper
6
+ module Reporters
7
+ # Turns a set of findings' reclaimable-bytes into a human sentence, and a
8
+ # single finding's into a table cell. Shared by every reporter that states
9
+ # a reclaim total in prose, so the answer cannot drift between formats --
10
+ # which is how the terminal report learned to distinguish "no bytes" from
11
+ # "no idea" while the Markdown report kept treating them as the same 0.
12
+ module Reclaim
13
+ module_function
14
+
15
+ # A row count is required for a byte estimate, and pg reports
16
+ # reltuples = -1 for a table that has never been analysed. Printing 0.0 B
17
+ # for an unknown reads as "nothing to gain here", a different claim.
18
+ def summary(findings)
19
+ total = findings.sum(&:reclaimable_bytes)
20
+ return "~#{Bytes.human(total)} reclaimable" if total.positive?
21
+ return nil unless unmeasured?(findings)
22
+
23
+ "reclaim estimate unavailable — run ANALYZE to populate table statistics"
24
+ end
25
+
26
+ # True when some finding's byte estimate is unknown rather than zero.
27
+ # bytes_per_row.zero? findings (missing_fk_index, duplicate_index -- they
28
+ # add or drop an index, not data) always reclaim 0 regardless of row
29
+ # count, so an unknown row count does not make their answer unmeasured.
30
+ def unmeasured?(findings)
31
+ findings.any? { |f| f.bytes_per_row.to_i.positive? && !f.reclaim_known? }
32
+ end
33
+
34
+ # Per-finding cell for a table row: the human size, or a note that it is
35
+ # not known, using the same bytes_per_row-zero exception as summary.
36
+ def cell(finding)
37
+ return Bytes.human(finding.reclaimable_bytes) if measured?(finding)
38
+
39
+ "unknown"
40
+ end
41
+
42
+ def measured?(finding)
43
+ finding.bytes_per_row.to_i.zero? || finding.reclaim_known?
44
+ end
45
+ end
46
+ end
47
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative "bytes"
4
4
  require_relative "ansi"
5
5
  require_relative "rollup"
6
+ require_relative "reclaim"
6
7
 
7
8
  module SchemaReaper
8
9
  module Reporters
@@ -57,18 +58,8 @@ module SchemaReaper
57
58
  .map { |t, n| "#{t} #{n}" }.join(" · ")
58
59
  end
59
60
 
60
- # A reclaim estimate needs a row count, and pg reports reltuples = -1 for
61
- # a table it has never analysed. Printing 0.0 B for an unknown reads as
62
- # "nothing to gain here", which is a different claim entirely.
63
61
  def reclaim_text
64
- return "~#{Bytes.human(total_reclaimable)} reclaimable" if total_reclaimable.positive?
65
- return nil unless unmeasured?
66
-
67
- "reclaim estimate unavailable — run ANALYZE to populate table statistics"
68
- end
69
-
70
- def unmeasured?
71
- @findings.any? { |f| f.bytes_per_row.to_i.positive? && !f.reclaim_known? }
62
+ Reclaim.summary(@findings)
72
63
  end
73
64
 
74
65
  def rolled_up
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SchemaReaper
4
- VERSION = "1.0.11"
4
+ VERSION = "1.0.12"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schema_reaper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.11
4
+ version: 1.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - aksshatt
@@ -130,6 +130,7 @@ files:
130
130
  - lib/schema_reaper/reporters/console.rb
131
131
  - lib/schema_reaper/reporters/json.rb
132
132
  - lib/schema_reaper/reporters/markdown.rb
133
+ - lib/schema_reaper/reporters/reclaim.rb
133
134
  - lib/schema_reaper/reporters/rollup.rb
134
135
  - lib/schema_reaper/reporters/sarif.rb
135
136
  - lib/schema_reaper/reporters/table.rb