gitlab-exporter 7.1.2 → 10.0.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: 350884c8d1f03a58cad2630e0076aa641d22d60aed65a5d2342cb7f4508ec655
4
- data.tar.gz: d1857b8e6a8f842b438d12810c110ff4b8bbdf5226b5a6c5859dd949c12d1dd6
3
+ metadata.gz: 0f7115d7a7e8604d15ad7734afd3280c22edf9f6911af159f9386f2a9d495701
4
+ data.tar.gz: 7b050b23851b8073da229a5347b0c22afd641a1420859f37f0a0b8a7a4113244
5
5
  SHA512:
6
- metadata.gz: 9b62aaf4f95b4da82b1e5bc86058b97c363a1b43ae7026589e30da6af47ccf69261ec279094574927180b270be358afbba8304d10bff3fe8fb047fb54b6be538
7
- data.tar.gz: 34d27b77c5b04c4a079d184ff30db9b75f37471f9ac0e5199790ce550ee360e52d6f81d2b22b6f159a780ff7525b866b79f8ab0c06265d6251f697177b86fc83
6
+ metadata.gz: 848007d860313f8d62239b3f97a9024944aec9ddbfe6ea97472eaae64ddfc6e03cc056fe9466ce719209a6fad2b5a88947ff085f25ed6cf4ecf6421c1db576d7
7
+ data.tar.gz: f751e8af1eea6d30eb00dae65ec41681b50e9481a392b6521fde15c07934d8550dd2c3e3b65ea13ae6cbc0adadfe30986bad418c88172699b3172d20f9f62a75
@@ -7,7 +7,7 @@ include:
7
7
  - template: Security/Secret-Detection.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml
8
8
 
9
9
  default:
10
- image: "ruby:2.3"
10
+ image: ruby:2.7
11
11
  cache:
12
12
  paths:
13
13
  - vendor
@@ -1,5 +1,7 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  AllCops:
2
- TargetRubyVersion: 2.3
4
+ TargetRubyVersion: 2.7
3
5
  DisplayCopNames: true
4
6
 
5
7
  # Just use double quotes please
@@ -20,15 +22,15 @@ Style/RaiseArgs:
20
22
  Style/FrozenStringLiteralComment:
21
23
  Enabled: false
22
24
 
25
+ # Commonly used screens these days easily fit more than 80 characters.
26
+ Layout/LineLength:
27
+ Max: 120
28
+
23
29
  Metrics/MethodLength:
24
30
  Max: 15
25
31
 
26
32
  Metrics/AbcSize:
27
33
  Enabled: false
28
34
 
29
- # Commonly used screens these days easily fit more than 80 characters.
30
- Metrics/LineLength:
31
- Max: 120
32
-
33
35
  Metrics/ClassLength:
34
36
  Max: 150
@@ -0,0 +1,64 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2021-01-11 12:24:03 UTC using RuboCop version 0.93.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: Include.
11
+ # Include: **/*.gemspec
12
+ Gemspec/RequiredRubyVersion:
13
+ Exclude:
14
+ - 'gitlab-exporter.gemspec'
15
+
16
+ # Offense count: 3
17
+ Lint/AmbiguousBlockAssociation:
18
+ Exclude:
19
+ - 'spec/git_spec.rb'
20
+ - 'spec/util_spec.rb'
21
+
22
+ # Offense count: 2
23
+ # Configuration parameters: MaximumRangeSize.
24
+ Lint/MissingCopEnableDirective:
25
+ Exclude:
26
+ - 'spec/database/ci_builds_spec.rb'
27
+ - 'spec/git_process_proper_spec.rb'
28
+
29
+ # Offense count: 1
30
+ # Cop supports --auto-correct.
31
+ Lint/NonDeterministicRequireOrder:
32
+ Exclude:
33
+ - 'spec/spec_helper.rb'
34
+
35
+ # Offense count: 16
36
+ # Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
37
+ # ExcludedMethods: refine
38
+ Metrics/BlockLength:
39
+ Max: 349
40
+
41
+ # Offense count: 1
42
+ # Configuration parameters: IgnoredMethods.
43
+ Metrics/CyclomaticComplexity:
44
+ Max: 8
45
+
46
+ # Offense count: 1
47
+ # Configuration parameters: IgnoredMethods.
48
+ Metrics/PerceivedComplexity:
49
+ Max: 9
50
+
51
+ # Offense count: 1
52
+ # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols.
53
+ # SupportedStyles: inline, group
54
+ Style/AccessModifierDeclarations:
55
+ Exclude:
56
+ - 'lib/gitlab_exporter/util.rb'
57
+
58
+ # Offense count: 2
59
+ # Cop supports --auto-correct.
60
+ # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods.
61
+ # AllowedMethods: present?, blank?, presence, try, try!
62
+ Style/SafeNavigation:
63
+ Exclude:
64
+ - 'lib/gitlab_exporter/database/base.rb'
@@ -0,0 +1 @@
1
+ 2.7.2
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab-exporter (7.1.2)
4
+ gitlab-exporter (10.0.0)
5
5
  connection_pool (~> 2.2.1)
6
6
  pg (~> 1.1)
7
+ puma (~> 5.1.1)
7
8
  quantile (~> 0.2.0)
8
9
  redis (~> 4.1.2)
9
10
  redis-namespace (~> 1.6.0)
@@ -13,23 +14,28 @@ PATH
13
14
  GEM
14
15
  remote: https://rubygems.org/
15
16
  specs:
16
- ast (2.4.0)
17
+ ast (2.4.1)
17
18
  connection_pool (2.2.3)
18
19
  diff-lcs (1.3)
19
20
  mustermann (1.1.1)
20
21
  ruby2_keywords (~> 0.0.1)
21
- parser (2.5.1.0)
22
- ast (~> 2.4.0)
22
+ nio4r (2.5.4)
23
+ parallel (1.20.1)
24
+ parser (3.0.0.0)
25
+ ast (~> 2.4.1)
23
26
  pg (1.2.3)
24
- powerpack (0.1.1)
27
+ puma (5.1.1)
28
+ nio4r (~> 2.0)
25
29
  quantile (0.2.1)
26
30
  rack (2.2.3)
27
31
  rack-protection (2.0.8.1)
28
32
  rack
29
- rainbow (2.1.0)
33
+ rainbow (3.0.0)
30
34
  redis (4.1.4)
31
35
  redis-namespace (1.6.0)
32
36
  redis (>= 3.0.4)
37
+ regexp_parser (2.0.3)
38
+ rexml (3.2.4)
33
39
  rspec (3.7.0)
34
40
  rspec-core (~> 3.7.0)
35
41
  rspec-expectations (~> 3.7.0)
@@ -43,14 +49,19 @@ GEM
43
49
  diff-lcs (>= 1.2.0, < 2.0)
44
50
  rspec-support (~> 3.7.0)
45
51
  rspec-support (3.7.1)
46
- rubocop (0.42.0)
47
- parser (>= 2.3.1.1, < 3.0)
48
- powerpack (~> 0.1)
49
- rainbow (>= 1.99.1, < 3.0)
52
+ rubocop (0.93.1)
53
+ parallel (~> 1.10)
54
+ parser (>= 2.7.1.5)
55
+ rainbow (>= 2.2.2, < 4.0)
56
+ regexp_parser (>= 1.8)
57
+ rexml
58
+ rubocop-ast (>= 0.6.0)
50
59
  ruby-progressbar (~> 1.7)
51
- unicode-display_width (~> 1.0, >= 1.0.1)
52
- ruby-progressbar (1.8.1)
53
- ruby2_keywords (0.0.2)
60
+ unicode-display_width (>= 1.4.0, < 2.0)
61
+ rubocop-ast (1.4.0)
62
+ parser (>= 2.7.1.5)
63
+ ruby-progressbar (1.11.0)
64
+ ruby2_keywords (0.0.4)
54
65
  sidekiq (5.2.9)
55
66
  connection_pool (~> 2.2, >= 2.2.2)
56
67
  rack (~> 2.0)
@@ -62,7 +73,7 @@ GEM
62
73
  rack-protection (= 2.0.8.1)
63
74
  tilt (~> 2.0)
64
75
  tilt (2.0.10)
65
- unicode-display_width (1.6.0)
76
+ unicode-display_width (1.7.0)
66
77
 
67
78
  PLATFORMS
68
79
  ruby
@@ -74,4 +85,4 @@ DEPENDENCIES
74
85
  rubocop (~> 0.42)
75
86
 
76
87
  BUNDLED WITH
77
- 1.17.3
88
+ 2.1.4
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
4
 
5
+ require "rubygems"
6
+ require "bundler/setup"
5
7
  require "optparse"
6
8
  require "gitlab_exporter"
7
9
 
@@ -6,6 +6,7 @@ db_common: &db_common
6
6
 
7
7
  # Web server config
8
8
  server:
9
+ name: puma # cf. https://github.com/sinatra/sinatra#available-settings
9
10
  listen_address: 0.0.0.0
10
11
  listen_port: 9168
11
12
  # Maximum amount of memory to use in megabytes, after which the process is killed
@@ -94,12 +95,21 @@ probes:
94
95
  redis_url: "redis://localhost:6379"
95
96
  redis_enable_client: true
96
97
 
98
+ ruby: &ruby
99
+ class_name: RubyProber
100
+ methods:
101
+ - probe_gc
102
+ opts:
103
+ quantiles: false
104
+
97
105
  metrics:
98
106
  multiple: true
99
107
  git_process:
100
108
  <<: *git_process
101
109
  process:
102
110
  <<: *process
111
+ ruby:
112
+ <<: *ruby
103
113
  sidekiq:
104
114
  <<: *sidekiq
105
115
  ci_builds:
@@ -1,4 +1,4 @@
1
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path("lib", __dir__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require "gitlab_exporter/version"
4
4
 
@@ -20,13 +20,14 @@ Gem::Specification.new do |s|
20
20
  s.homepage = "http://gitlab.com"
21
21
  s.license = "MIT"
22
22
 
23
+ s.add_runtime_dependency "connection_pool", "~> 2.2.1"
23
24
  s.add_runtime_dependency "pg", "~> 1.1"
24
- s.add_runtime_dependency "sinatra", "~> 2.0.4"
25
+ s.add_runtime_dependency "puma", "~> 5.1.1"
25
26
  s.add_runtime_dependency "quantile", "~> 0.2.0"
26
- s.add_runtime_dependency "sidekiq", "~> 5.2.1"
27
27
  s.add_runtime_dependency "redis", "~> 4.1.2"
28
28
  s.add_runtime_dependency "redis-namespace", "~> 1.6.0"
29
- s.add_runtime_dependency "connection_pool", "~> 2.2.1"
29
+ s.add_runtime_dependency "sidekiq", "~> 5.2.1"
30
+ s.add_runtime_dependency "sinatra", "~> 2.0.4"
30
31
 
31
32
  s.add_development_dependency "rspec", "~> 3.7.0"
32
33
  s.add_development_dependency "rspec-expectations", "~> 3.7.0"
@@ -14,5 +14,6 @@ module GitLab
14
14
  autoload :WebExporter, "gitlab_exporter/web_exporter"
15
15
  autoload :Prober, "gitlab_exporter/prober"
16
16
  autoload :SidekiqProber, "gitlab_exporter/sidekiq"
17
+ autoload :RubyProber, "gitlab_exporter/ruby"
17
18
  end
18
19
  end
@@ -19,8 +19,7 @@ module GitLab
19
19
  # Empty runner that will raise an InvalidCLICommand when executed to provide the usage
20
20
  # in the exception message
21
21
  class NullRunner
22
- def initialize(args)
23
- end
22
+ def initialize(args); end
24
23
 
25
24
  def run
26
25
  fail InvalidCLICommand.new("Usage: #{EXECUTABLE_NAME} <command> [options] [arguments...]\n\n"\
@@ -8,14 +8,39 @@ module GitLab
8
8
  #
9
9
  # It takes a connection string (e.g. "dbname=test port=5432")
10
10
  class Base
11
+ POOL_SIZE = 3
12
+
13
+ # This timeout is configured to higher interval than scrapping
14
+ # of Prometheus to ensure that connection is kept instead of
15
+ # needed to be re-initialized
16
+ POOL_TIMEOUT = 90
17
+
11
18
  def self.connection_pool
12
- @connection_pool ||= Hash.new do |h, connection_string|
13
- h[connection_string] = ConnectionPool.new(size: 3, timeout: 5) do
14
- PG.connect(connection_string)
19
+ @@connection_pool ||= Hash.new do |h, connection_string| # rubocop:disable Style/ClassVars
20
+ h[connection_string] = ConnectionPool.new(size: POOL_SIZE, timeout: POOL_TIMEOUT) do
21
+ PG.connect(connection_string).tap do |conn|
22
+ configure_type_map_for_results(conn)
23
+ end
15
24
  end
16
25
  end
17
26
  end
18
27
 
28
+ def self.configure_type_map_for_results(conn)
29
+ tm = PG::BasicTypeMapForResults.new(conn)
30
+
31
+ # Remove warning message:
32
+ # Warning: no type cast defined for type "name" with oid 19.
33
+ # Please cast this type explicitly to TEXT to be safe for future changes.
34
+ # Warning: no type cast defined for type "regproc" with oid 24.
35
+ # Please cast this type explicitly to TEXT to be safe for future changes.
36
+ [{ "type": "text", "oid": 19 }, { "type": "int4", "oid": 24 }].each do |value|
37
+ old_coder = tm.coders.find { |c| c.name == value[:type] }
38
+ tm.add_coder(old_coder.dup.tap { |c| c.oid = value[:oid] })
39
+ end
40
+
41
+ conn.type_map_for_results = tm
42
+ end
43
+
19
44
  def initialize(args, logger: nil)
20
45
  @connection_string = args[:connection_string]
21
46
  @logger = logger
@@ -31,13 +56,11 @@ module GitLab
31
56
 
32
57
  def with_connection_pool
33
58
  connection_pool.with do |conn|
34
- begin
35
- yield conn
36
- rescue PG::UnableToSend => e
37
- @logger.warn "Error sending to the database: #{e}" if @logger
38
- conn.reset
39
- raise e
40
- end
59
+ yield conn
60
+ rescue PG::UnableToSend => e
61
+ @logger.warn "Error sending to the database: #{e}" if @logger
62
+ conn.reset
63
+ raise e
41
64
  end
42
65
  rescue PG::Error => e
43
66
  @logger.error "Error connecting to the database: #{e}" if @logger
@@ -35,7 +35,7 @@ module GitLab
35
35
 
36
36
  # Prober class to gather bloat metrics
37
37
  class BloatProber
38
- METRIC_KEYS = %w(bloat_ratio bloat_size extra_size real_size).freeze
38
+ METRIC_KEYS = %w[bloat_ratio bloat_size extra_size real_size].freeze
39
39
 
40
40
  attr_reader :metrics, :collector, :bloat_types
41
41
 
@@ -46,7 +46,7 @@ module GitLab
46
46
  @metrics = metrics
47
47
  @collector = collector
48
48
  @collector.logger = logger
49
- @bloat_types = opts[:bloat_types] || %i(btree table)
49
+ @bloat_types = opts[:bloat_types] || %i[btree table]
50
50
  end
51
51
 
52
52
  def probe_db
@@ -12,73 +12,90 @@ SELECT current_database(), nspname AS schemaname, tblname, idxname AS object_nam
12
12
  END AS bloat_size,
13
13
  100 * (relpages-est_pages_ff)::float / relpages AS bloat_ratio,
14
14
  is_na
15
- -- , 100-(sub.pst).avg_leaf_density, est_pages, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, sub.reltuples, sub.relpages -- (DEBUG INFO)
15
+ -- , 100-(pst).avg_leaf_density AS pst_avg_bloat, est_pages, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples, relpages -- (DEBUG INFO)
16
16
  FROM (
17
17
  SELECT coalesce(1 +
18
- ceil(reltuples/floor((bs-pageopqdata-pagehdr)/(4+nulldatahdrwidth)::float)), 0 -- ItemIdData size + computed avg size of a tuple (nulldatahdrwidth)
19
- ) AS est_pages,
20
- coalesce(1 +
21
- ceil(reltuples/floor((bs-pageopqdata-pagehdr)*fillfactor/(100*(4+nulldatahdrwidth)::float))), 0
22
- ) AS est_pages_ff,
23
- bs, nspname, table_oid, tblname, idxname, relpages, fillfactor, is_na
24
- -- , stattuple.pgstatindex(quote_ident(nspname)||'.'||quote_ident(idxname)) AS pst, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples -- (DEBUG INFO)
18
+ ceil(reltuples/floor((bs-pageopqdata-pagehdr)/(4+nulldatahdrwidth)::float)), 0 -- ItemIdData size + computed avg size of a tuple (nulldatahdrwidth)
19
+ ) AS est_pages,
20
+ coalesce(1 +
21
+ ceil(reltuples/floor((bs-pageopqdata-pagehdr)*fillfactor/(100*(4+nulldatahdrwidth)::float))), 0
22
+ ) AS est_pages_ff,
23
+ bs, nspname, tblname, idxname, relpages, fillfactor, is_na
24
+ -- , pgstatindex(idxoid) AS pst, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples -- (DEBUG INFO)
25
25
  FROM (
26
- SELECT maxalign, bs, nspname, tblname, idxname, reltuples, relpages, relam, table_oid, fillfactor,
27
- ( index_tuple_hdr_bm +
28
- maxalign - CASE -- Add padding to the index tuple header to align on MAXALIGN
29
- WHEN index_tuple_hdr_bm%maxalign = 0 THEN maxalign
30
- ELSE index_tuple_hdr_bm%maxalign
31
- END
32
- + nulldatawidth + maxalign - CASE -- Add padding to the data to align on MAXALIGN
33
- WHEN nulldatawidth = 0 THEN 0
34
- WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign
35
- ELSE nulldatawidth::integer%maxalign
36
- END
37
- )::numeric AS nulldatahdrwidth, pagehdr, pageopqdata, is_na
38
- -- , index_tuple_hdr_bm, nulldatawidth -- (DEBUG INFO)
39
- FROM (
40
- SELECT
41
- i.nspname, i.tblname, i.idxname, i.reltuples, i.relpages, i.relam, a.attrelid AS table_oid,
42
- current_setting('block_size')::numeric AS bs, fillfactor,
43
- CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
44
- WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8
45
- ELSE 4
46
- END AS maxalign,
47
- /* per page header, fixed size: 20 for 7.X, 24 for others */
48
- 24 AS pagehdr,
49
- /* per page btree opaque data */
50
- 16 AS pageopqdata,
51
- /* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
52
- CASE WHEN max(coalesce(s.null_frac,0)) = 0
53
- THEN 2 -- IndexTupleData size
54
- ELSE 2 + (( 32 + 8 - 1 ) / 8) -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8)
55
- END AS index_tuple_hdr_bm,
56
- /* data len: we remove null values save space using it fractionnal part from stats */
57
- sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) AS nulldatawidth,
58
- max( CASE WHEN a.atttypid = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END ) > 0 AS is_na
59
- FROM pg_attribute AS a
60
- JOIN (
61
- SELECT nspname, tbl.relname AS tblname, idx.relname AS idxname, idx.reltuples, idx.relpages, idx.relam,
62
- indrelid, indexrelid, indkey::smallint[] AS attnum,
63
- coalesce(substring(
64
- array_to_string(idx.reloptions, ' ')
65
- from 'fillfactor=([0-9]+)')::smallint, 90) AS fillfactor
66
- FROM pg_index
67
- JOIN pg_class idx ON idx.oid=pg_index.indexrelid
68
- JOIN pg_class tbl ON tbl.oid=pg_index.indrelid
69
- JOIN pg_namespace ON pg_namespace.oid = idx.relnamespace
70
- WHERE pg_index.indisvalid AND tbl.relkind = 'r' AND idx.relpages > 0
71
- ) AS i ON a.attrelid = i.indexrelid
72
- JOIN pg_stats AS s ON s.schemaname = i.nspname
73
- AND ((s.tablename = i.tblname AND s.attname = pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE)) -- stats from tbl
74
- OR (s.tablename = i.idxname AND s.attname = a.attname))-- stats from functionnal cols
75
- JOIN pg_type AS t ON a.atttypid = t.oid
76
- WHERE a.attnum > 0
77
- GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9
78
- ) AS s1
79
- ) AS s2
80
- JOIN pg_am am ON s2.relam = am.oid WHERE am.amname = 'btree'
81
- ) AS sub
82
- WHERE NOT is_na
83
- AND nspname = 'public'
84
- ORDER BY 2,3,4;
26
+ SELECT maxalign, bs, nspname, tblname, idxname, reltuples, relpages, idxoid, fillfactor,
27
+ ( index_tuple_hdr_bm +
28
+ maxalign - CASE -- Add padding to the index tuple header to align on MAXALIGN
29
+ WHEN index_tuple_hdr_bm%maxalign = 0 THEN maxalign
30
+ ELSE index_tuple_hdr_bm%maxalign
31
+ END
32
+ + nulldatawidth + maxalign - CASE -- Add padding to the data to align on MAXALIGN
33
+ WHEN nulldatawidth = 0 THEN 0
34
+ WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign
35
+ ELSE nulldatawidth::integer%maxalign
36
+ END
37
+ )::numeric AS nulldatahdrwidth, pagehdr, pageopqdata, is_na
38
+ -- , index_tuple_hdr_bm, nulldatawidth -- (DEBUG INFO)
39
+ FROM (
40
+ SELECT n.nspname, i.tblname, i.idxname, i.reltuples, i.relpages,
41
+ i.idxoid, i.fillfactor, current_setting('block_size')::numeric AS bs,
42
+ CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
43
+ WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8
44
+ ELSE 4
45
+ END AS maxalign,
46
+ /* per page header, fixed size: 20 for 7.X, 24 for others */
47
+ 24 AS pagehdr,
48
+ /* per page btree opaque data */
49
+ 16 AS pageopqdata,
50
+ /* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
51
+ CASE WHEN max(coalesce(s.null_frac,0)) = 0
52
+ THEN 2 -- IndexTupleData size
53
+ ELSE 2 + (( 32 + 8 - 1 ) / 8) -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8)
54
+ END AS index_tuple_hdr_bm,
55
+ /* data len: we remove null values save space using it fractionnal part from stats */
56
+ sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) AS nulldatawidth,
57
+ max( CASE WHEN i.atttypid = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END ) > 0 AS is_na
58
+ FROM (
59
+ SELECT ct.relname AS tblname, ct.relnamespace, ic.idxname, ic.attpos, ic.indkey, ic.indkey[ic.attpos], ic.reltuples, ic.relpages, ic.tbloid, ic.idxoid, ic.fillfactor,
60
+ coalesce(a1.attnum, a2.attnum) AS attnum, coalesce(a1.attname, a2.attname) AS attname, coalesce(a1.atttypid, a2.atttypid) AS atttypid,
61
+ CASE WHEN a1.attnum IS NULL
62
+ THEN ic.idxname
63
+ ELSE ct.relname
64
+ END AS attrelname
65
+ FROM (
66
+ SELECT idxname, reltuples, relpages, tbloid, idxoid, fillfactor, indkey,
67
+ pg_catalog.generate_series(1,indnatts) AS attpos
68
+ FROM (
69
+ SELECT ci.relname AS idxname, ci.reltuples, ci.relpages, i.indrelid AS tbloid,
70
+ i.indexrelid AS idxoid,
71
+ coalesce(substring(
72
+ array_to_string(ci.reloptions, ' ')
73
+ from 'fillfactor=([0-9]+)')::smallint, 90) AS fillfactor,
74
+ i.indnatts,
75
+ pg_catalog.string_to_array(pg_catalog.textin(
76
+ pg_catalog.int2vectorout(i.indkey)),' ')::int[] AS indkey
77
+ FROM pg_catalog.pg_index i
78
+ JOIN pg_catalog.pg_class ci ON ci.oid = i.indexrelid
79
+ WHERE ci.relam=(SELECT oid FROM pg_am WHERE amname = 'btree')
80
+ AND ci.relpages > 0
81
+ ) AS idx_data
82
+ ) AS ic
83
+ JOIN pg_catalog.pg_class ct ON ct.oid = ic.tbloid
84
+ LEFT JOIN pg_catalog.pg_attribute a1 ON
85
+ ic.indkey[ic.attpos] <> 0
86
+ AND a1.attrelid = ic.tbloid
87
+ AND a1.attnum = ic.indkey[ic.attpos]
88
+ LEFT JOIN pg_catalog.pg_attribute a2 ON
89
+ ic.indkey[ic.attpos] = 0
90
+ AND a2.attrelid = ic.idxoid
91
+ AND a2.attnum = ic.attpos
92
+ ) i
93
+ JOIN pg_catalog.pg_namespace n ON n.oid = i.relnamespace
94
+ JOIN pg_catalog.pg_stats s ON s.schemaname = n.nspname
95
+ AND s.tablename = i.attrelname
96
+ AND s.attname = i.attname
97
+ GROUP BY 1,2,3,4,5,6,7,8,9,10,11
98
+ ) AS rows_data_stats
99
+ ) AS rows_hdr_pdg_stats
100
+ ) AS relation_stats
101
+ ORDER BY nspname, tblname, idxname;