pg_reports 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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +122 -377
  4. data/app/controllers/pg_reports/dashboard_controller.rb +21 -21
  5. data/app/views/layouts/pg_reports/application.html.erb +65 -8
  6. data/app/views/pg_reports/dashboard/_show_modals.html.erb +22 -22
  7. data/app/views/pg_reports/dashboard/_show_scripts.html.erb +55 -57
  8. data/app/views/pg_reports/dashboard/_show_styles.html.erb +18 -0
  9. data/app/views/pg_reports/dashboard/index.html.erb +109 -106
  10. data/app/views/pg_reports/dashboard/show.html.erb +26 -26
  11. data/config/locales/en.yml +488 -0
  12. data/config/locales/ru.yml +481 -0
  13. data/config/locales/uk.yml +481 -0
  14. data/lib/pg_reports/compatibility.rb +3 -3
  15. data/lib/pg_reports/dashboard/reports_registry.rb +83 -12
  16. data/lib/pg_reports/definitions/schema_analysis/always_null_columns.yml +31 -0
  17. data/lib/pg_reports/definitions/schema_analysis/unused_columns.yml +32 -0
  18. data/lib/pg_reports/definitions/tables/unused_tables.yml +30 -0
  19. data/lib/pg_reports/definitions/tables/update_hotspots.yml +32 -0
  20. data/lib/pg_reports/module_generator.rb +2 -1
  21. data/lib/pg_reports/modules/schema_analysis.rb +261 -2
  22. data/lib/pg_reports/modules/system.rb +3 -3
  23. data/lib/pg_reports/query_monitor.rb +2 -6
  24. data/lib/pg_reports/report_definition.rb +20 -24
  25. data/lib/pg_reports/sql/schema_analysis/always_null_columns.sql +25 -0
  26. data/lib/pg_reports/sql/schema_analysis/unused_columns.sql +36 -0
  27. data/lib/pg_reports/sql/tables/unused_tables.sql +19 -0
  28. data/lib/pg_reports/sql/tables/update_hotspots.sql +26 -0
  29. data/lib/pg_reports/version.rb +1 -1
  30. metadata +9 -1
@@ -0,0 +1,25 @@
1
+ -- Columns where ~100% of rows are NULL
2
+ -- Strong indicator nothing in the application populates this column anymore.
3
+
4
+ SELECT
5
+ s.schemaname AS schema,
6
+ s.tablename AS table_name,
7
+ s.attname AS column_name,
8
+ format_type(a.atttypid, a.atttypmod) AS data_type,
9
+ ROUND((s.null_frac * 100)::numeric, 2) AS null_pct,
10
+ pg_get_expr(ad.adbin, ad.adrelid) AS column_default,
11
+ a.attnotnull AS not_null_constraint,
12
+ c.reltuples::bigint AS estimated_rows
13
+ FROM pg_stats s
14
+ JOIN pg_class c ON c.relname = s.tablename
15
+ JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = s.schemaname
16
+ JOIN pg_attribute a ON a.attrelid = c.oid AND a.attname = s.attname
17
+ LEFT JOIN pg_attrdef ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
18
+ WHERE s.schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
19
+ AND c.relkind = 'r'
20
+ AND a.attnum > 0
21
+ AND NOT a.attisdropped
22
+ AND a.attnotnull = false
23
+ AND s.null_frac >= 0.999
24
+ AND c.reltuples > 1000
25
+ ORDER BY c.reltuples DESC;
@@ -0,0 +1,36 @@
1
+ -- Columns that have only ever held a single value (likely never updated since creation)
2
+ -- Uses pg_stats.n_distinct = 1 as a proxy for "no UPDATE has ever changed this field".
3
+ -- Strong indicator the application code no longer references the column but it was never dropped.
4
+
5
+ SELECT
6
+ s.schemaname AS schema,
7
+ s.tablename AS table_name,
8
+ s.attname AS column_name,
9
+ format_type(a.atttypid, a.atttypmod) AS data_type,
10
+ CASE
11
+ WHEN s.most_common_vals IS NOT NULL
12
+ THEN substring((s.most_common_vals::text) FROM 1 FOR 80)
13
+ ELSE NULL
14
+ END AS sole_value,
15
+ pg_get_expr(ad.adbin, ad.adrelid) AS column_default,
16
+ ROUND((s.null_frac * 100)::numeric, 2) AS null_pct,
17
+ c.reltuples::bigint AS estimated_rows
18
+ FROM pg_stats s
19
+ JOIN pg_class c ON c.relname = s.tablename
20
+ JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = s.schemaname
21
+ JOIN pg_attribute a ON a.attrelid = c.oid AND a.attname = s.attname
22
+ LEFT JOIN pg_attrdef ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
23
+ WHERE s.schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
24
+ AND c.relkind = 'r'
25
+ AND a.attnum > 0
26
+ AND NOT a.attisdropped
27
+ AND s.n_distinct = 1
28
+ AND s.null_frac < 0.999
29
+ AND c.reltuples > 1000
30
+ AND NOT EXISTS (
31
+ SELECT 1 FROM pg_index i
32
+ WHERE i.indrelid = c.oid
33
+ AND a.attnum = ANY(i.indkey)
34
+ AND (i.indisprimary OR i.indisunique)
35
+ )
36
+ ORDER BY c.reltuples DESC;
@@ -0,0 +1,19 @@
1
+ -- Tables that have not been read at all since the last stats reset
2
+ -- Zero seq_scan AND zero idx_scan -- application code never queries them.
3
+ -- Candidates for archival, deletion, or extraction to a separate database.
4
+
5
+ SELECT
6
+ t.schemaname AS schema,
7
+ t.relname AS table_name,
8
+ t.n_live_tup AS live_rows,
9
+ pg_size_pretty(pg_total_relation_size(t.relid)) AS total_size,
10
+ ROUND(pg_total_relation_size(t.relid) / 1024.0 / 1024.0, 2) AS total_size_mb,
11
+ COALESCE(t.last_autoanalyze, t.last_analyze) AS last_analyzed,
12
+ d.stats_reset AS db_stats_since
13
+ FROM pg_stat_user_tables t
14
+ LEFT JOIN pg_stat_database d ON d.datname = current_database()
15
+ WHERE t.schemaname NOT IN ('pg_catalog', 'information_schema')
16
+ AND t.seq_scan = 0
17
+ AND COALESCE(t.idx_scan, 0) = 0
18
+ AND t.n_live_tup > 0
19
+ ORDER BY pg_total_relation_size(t.relid) DESC;
@@ -0,0 +1,26 @@
1
+ -- Tables with disproportionately high write activity
2
+ -- updates_per_row = n_tup_upd / n_live_tup -- same rows being updated repeatedly
3
+ -- hot_update_pct = n_tup_hot_upd / n_tup_upd -- low value means indexed columns are being updated (expensive)
4
+
5
+ SELECT
6
+ schemaname AS schema,
7
+ relname AS table_name,
8
+ n_live_tup AS live_rows,
9
+ n_tup_upd AS updates,
10
+ n_tup_hot_upd AS hot_updates,
11
+ CASE WHEN n_tup_upd > 0
12
+ THEN ROUND((n_tup_hot_upd::numeric / n_tup_upd) * 100, 2)
13
+ ELSE 0
14
+ END AS hot_update_pct,
15
+ CASE WHEN n_live_tup > 0
16
+ THEN ROUND(n_tup_upd::numeric / n_live_tup, 2)
17
+ ELSE 0
18
+ END AS updates_per_row,
19
+ n_tup_ins AS inserts,
20
+ n_tup_del AS deletes,
21
+ n_dead_tup AS dead_rows
22
+ FROM pg_stat_user_tables
23
+ WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
24
+ AND n_tup_upd > 1000
25
+ AND n_live_tup > 100
26
+ ORDER BY (n_tup_upd::numeric / GREATEST(n_live_tup, 1)) DESC;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgReports
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_reports
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
  - Eldar Avatov
@@ -191,6 +191,8 @@ files:
191
191
  - lib/pg_reports/definitions/queries/missing_index_queries.yml
192
192
  - lib/pg_reports/definitions/queries/slow_queries.yml
193
193
  - lib/pg_reports/definitions/queries/temp_file_queries.yml
194
+ - lib/pg_reports/definitions/schema_analysis/always_null_columns.yml
195
+ - lib/pg_reports/definitions/schema_analysis/unused_columns.yml
194
196
  - lib/pg_reports/definitions/system/activity_overview.yml
195
197
  - lib/pg_reports/definitions/system/cache_stats.yml
196
198
  - lib/pg_reports/definitions/system/database_sizes.yml
@@ -204,6 +206,8 @@ files:
204
206
  - lib/pg_reports/definitions/tables/seq_scans.yml
205
207
  - lib/pg_reports/definitions/tables/table_sizes.yml
206
208
  - lib/pg_reports/definitions/tables/tables_without_pk.yml
209
+ - lib/pg_reports/definitions/tables/unused_tables.yml
210
+ - lib/pg_reports/definitions/tables/update_hotspots.yml
207
211
  - lib/pg_reports/definitions/tables/vacuum_needed.yml
208
212
  - lib/pg_reports/engine.rb
209
213
  - lib/pg_reports/error.rb
@@ -248,7 +252,9 @@ files:
248
252
  - lib/pg_reports/sql/queries/missing_index_queries.sql
249
253
  - lib/pg_reports/sql/queries/slow_queries.sql
250
254
  - lib/pg_reports/sql/queries/temp_file_queries.sql
255
+ - lib/pg_reports/sql/schema_analysis/always_null_columns.sql
251
256
  - lib/pg_reports/sql/schema_analysis/unique_indexes.sql
257
+ - lib/pg_reports/sql/schema_analysis/unused_columns.sql
252
258
  - lib/pg_reports/sql/system/activity_overview.sql
253
259
  - lib/pg_reports/sql/system/cache_stats.sql
254
260
  - lib/pg_reports/sql/system/checkpoint_stats.sql
@@ -266,6 +272,8 @@ files:
266
272
  - lib/pg_reports/sql/tables/seq_scans.sql
267
273
  - lib/pg_reports/sql/tables/table_sizes.sql
268
274
  - lib/pg_reports/sql/tables/tables_without_pk.sql
275
+ - lib/pg_reports/sql/tables/unused_tables.sql
276
+ - lib/pg_reports/sql/tables/update_hotspots.sql
269
277
  - lib/pg_reports/sql/tables/vacuum_needed.sql
270
278
  - lib/pg_reports/sql_loader.rb
271
279
  - lib/pg_reports/telegram_sender.rb