Skip to content
This repository was archived by the owner on Jul 6, 2021. It is now read-only.

Commit d70cdcf

Browse files
committed
Merge branch 'master' into 'dmius-hot-a006-fix'
# Conflicts: # pghrep/templates/A006.tpl
2 parents 78af660 + f919b07 commit d70cdcf

14 files changed

+203
-89
lines changed

.ci/h003_step2.sql

-1
This file was deleted.

.ci/h003.sql renamed to .ci/h003_step_1.sql

+2
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ create table t1 as select id::int8 from generate_series(0, 1000000) _(id);
22
alter table t1 add primary key (id);
33
create table t2 as select id, (random() * 1000000)::int8 as t1_id from generate_series(1, 1000000) _(id);
44
alter table t2 add constraint fk_t2_t1 foreign key (t1_id) references t1(id);
5+
vacuum analyze t1;
6+
vacuum analyze t2;

.ci/h003_step_2.sql

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
create index on t2 using btree (t1_id);
2+
vacuum analyze t2;

.ci/test_db_dump.sql

+13-6
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,26 @@ create index concurrently t_with_redundant_idx_f2 on t_with_redundant_idx(f2);
3131
create index concurrently t_with_redundant_idx_f3_1 on t_with_redundant_idx(f3);
3232
create index concurrently t_with_redundant_idx_f3_2 on t_with_redundant_idx(f3);
3333

34+
drop table if exists public.t_with_redundant_ref_idx;
35+
create table public.t_with_redundant_ref_idx as select i from generate_series(1, 1000000) _(i);
36+
create index t_with_redundant_ref_idx_1 on public.t_with_redundant_ref_idx using btree (i);
37+
create index t_with_redundant_ref_idx_2 on public.t_with_redundant_ref_idx using btree (i);
38+
create index t_with_redundant_ref_idx_3 on public.t_with_redundant_ref_idx using btree (i);
39+
40+
create schema exp_redundant;
41+
drop table if exists exp_redundant.t_with_redundant_ref_idx;
42+
create table exp_redundant.t_with_redundant_ref_idx as select i from generate_series(1, 1000000) _(i);
43+
create index t_with_redundant_ref_idx_1 on exp_redundant.t_with_redundant_ref_idx using btree (i);
44+
create index t_with_redundant_ref_idx_2 on exp_redundant.t_with_redundant_ref_idx using btree (i);
45+
create index t_with_redundant_ref_idx_3 on exp_redundant.t_with_redundant_ref_idx using btree (i);
46+
3447
-- H001 invalid indexes
3548
create schema test_schema;
3649
create table test_schema.t_with_invalid_index as select i from generate_series(1, 1000000) _(i);
3750
set statement_timeout to '20ms';
3851
create index concurrently i_invalid on test_schema.t_with_invalid_index(i);
3952
set statement_timeout to 0;
4053

41-
-- H003 non indexed fks
42-
create table t_fk_1 as select id::int8 from generate_series(0, 1000000) _(id);
43-
alter table t_fk_1 add primary key (id);
44-
create table t_fk_2 as select id, (random() * 1000000)::int8 as t1_id from generate_series(1, 1000000) _(id);
45-
alter table t_fk_2 add constraint fk_t2_t1 foreign key (t1_id) references t_fk_1(id);
46-
4754
-- Bloat level
4855
create table bloated as select i from generate_series(1, 100000) _(i);
4956
create index i_bloated on bloated(i);

.gitlab-ci.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ before_script:
2525
script:
2626
- psql -h postgres -d dbname -U username -c "SELECT version();"
2727
- echo "Test H003 Non indexed FKs"
28-
- psql -h postgres -d dbname -U username -f .ci/h003.sql
28+
- psql -h postgres -d dbname -U username -f .ci/h003_step_1.sql
2929
- ./checkup -h postgres --username username --project test --dbname dbname -e 1 --file ./resources/checks/H003_non_indexed_fks.sh
3030
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H003_non_indexed_fks.json | jq '.results .postgres .data') && ([[ "$result" == "[]" ]] || [[ "$result" == "null" ]]) && exit 301
31-
- psql -h postgres -d dbname -U username -f .ci/h003_step2.sql
31+
- psql -h postgres -d dbname -U username -f .ci/h003_step_2.sql
3232
- rm -Rf ./artifacts/
3333
- ./checkup -h postgres --username username --project test --dbname dbname -e 1 --file ./resources/checks/H003_non_indexed_fks.sh
3434
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H003_non_indexed_fks.json | jq '.results .postgres .data') && echo "$result" && cat ./artifacts/test/json_reports/$data_dir/H003_non_indexed_fks.json && (! [[ "$result" == "[]" ]]) && exit 302
@@ -40,6 +40,10 @@ before_script:
4040
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."public.t_with_redundant_idx_f1"') && ( [[ "$result" == "" ]] || [[ "$result" == "null" ]]) && exit 202
4141
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."public.t_with_redundant_idx_f1_uniq"') && ([[ ! "$result" == "[]" ]] && [[ ! "$result" == "null" ]]) && exit 203
4242
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."public.t_with_redundant_idx_pkey"') && ([[ ! "$result" == "[]" ]] && [[ ! "$result" == "null" ]]) && exit 204
43+
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."public.t_with_redundant_ref_idx_1"') && ([[ ! "$result" == "[]" ]] && [[ ! "$result" == "null" ]]) && exit 205
44+
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."public.t_with_redundant_ref_idx_2"') && ( [[ "$result" == "" ]] || [[ "$result" == "null" ]]) && exit 206
45+
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."exp_redundant.t_with_redundant_ref_idx_1"') && ([[ ! "$result" == "[]" ]] && [[ ! "$result" == "null" ]]) && exit 207
46+
- data_dir=$(cat ./artifacts/test/nodes.json | jq -r '.last_check | .dir') && result=$(cat ./artifacts/test/json_reports/$data_dir/H002_unused_indexes.json | jq '.results .postgres .data .redundant_indexes ."exp_redundant.t_with_redundant_ref_idx_2"') && ( [[ "$result" == "" ]] || [[ "$result" == "null" ]]) && exit 208
4347
- echo "H002 Passed"
4448

4549
test-general:

checkup

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ validate_args() {
352352
# fill default (not given) psql connection related variables
353353
[[ "${PGPORT}" = "None" ]] && export PGPORT=5432
354354
[[ "${DBNAME}" = "None" ]] && export DBNAME=postgres
355-
[[ "${STIMEOUT}" = "None" ]] && export STIMEOUT=15 # statement timeout
355+
[[ "${STIMEOUT}" = "None" ]] && export STIMEOUT=30 # statement timeout
356356
[[ "${USERNAME}" = "None" ]] && export USERNAME=""
357357

358358
# custom UNIX domain socket directory for PostgreSQL

pghrep/Makefile

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ test:
3838
go test ./src/fmtutils/
3939

4040
vet:
41-
go vet ./...
41+
# Command go vet examine all go files in a single scope, which isn't suitable for plugins.
42+
# We will run it separately for plugins and main sources.
43+
@for f in $(basename $(patsubst plugins/%.go,%,$(PLUGINS_SRC))); do \
44+
go vet ./plugins/$$f.go || exit 1 ; \
45+
done
46+
go vet ./src/...
4247

4348
fmt:
4449
go fmt $$(go list ./... | grep -v /vendor/)

pghrep/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
Pghrep used by `checkup` during database analysis to create markdown reports from JSON reports.
2+
13
To build report generator use:
24

35
`cd /pghrep`
4-
make all`
6+
`make all`
57

68
To generate report use:
79

resources/checks/F004_heap_bloat.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ with data as (
3131
join pg_namespace as ns on ns.oid = tbl.relnamespace
3232
join pg_stats as s on s.schemaname = ns.nspname and s.tablename = tbl.relname and not s.inherited and s.attname = att.attname
3333
left join pg_class as toast on tbl.reltoastrelid = toast.oid
34-
where att.attnum > 0 and not att.attisdropped and s.schemaname not in ('pg_catalog', 'information_schema')
34+
where att.attnum > 0 and not att.attisdropped and s.schemaname not in ('pg_catalog', 'information_schema') and tbl.relpages > 10
3535
group by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, tbl.relhasoids
3636
order by 2, 3
3737
), step2 as (

resources/checks/F005_index_bloat.sh

+16-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ with data as (
99
from pg_class pc
1010
join pg_namespace pn on pn.oid = pc.relnamespace
1111
where reloptions::text ~ 'autovacuum'
12+
), step0 as (
13+
select
14+
tbl.oid tblid, nspname, tbl.relname AS tblname, idx.relname AS idxname, idx.reltuples, idx.relpages, idx.relam,
15+
indrelid, indexrelid, regexp_split_to_table(indkey::text, ' ')::smallint AS attnum, --indkey::smallint[] AS attnum,
16+
coalesce(substring(array_to_string(idx.reloptions, ' ') from 'fillfactor=([0-9]+)')::smallint, 90) as fillfactor
17+
from pg_index
18+
join pg_class idx on idx.oid = pg_index.indexrelid
19+
join pg_class tbl on tbl.oid = pg_index.indrelid
20+
join pg_namespace on pg_namespace.oid = idx.relnamespace
21+
join pg_am a ON idx.relam = a.oid
22+
where a.amname = 'btree'
23+
AND pg_index.indisvalid
24+
AND tbl.relkind = 'r'
25+
AND idx.relpages > 10
26+
AND pg_namespace.nspname NOT IN ('pg_catalog', 'information_schema')
1227
), step1 as (
1328
select
1429
i.tblid,
@@ -36,17 +51,7 @@ with data as (
3651
sum((1 - coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) as nulldatawidth,
3752
max(case when a.atttypid = 'pg_catalog.name'::regtype then 1 else 0 end) > 0 as is_na
3853
from pg_attribute as a
39-
join (
40-
select
41-
tbl.oid tblid, nspname, tbl.relname AS tblname, idx.relname AS idxname, idx.reltuples, idx.relpages, idx.relam,
42-
indrelid, indexrelid, indkey::smallint[] AS attnum,
43-
coalesce(substring(array_to_string(idx.reloptions, ' ') from 'fillfactor=([0-9]+)')::smallint, 90) as fillfactor
44-
from pg_index
45-
join pg_class idx on idx.oid = pg_index.indexrelid
46-
join pg_class tbl on tbl.oid = pg_index.indrelid
47-
join pg_namespace on pg_namespace.oid = idx.relnamespace
48-
where pg_index.indisvalid AND tbl.relkind = 'r' AND idx.relpages > 0
49-
) as i on a.attrelid = i.indexrelid
54+
join step0 as i on a.attrelid = i.indexrelid AND a.attnum = i.attnum
5055
join pg_stats as s on
5156
s.schemaname = i.nspname
5257
and (

resources/checks/H002_unused_indexes.sh

+73-45
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,61 @@
11
${CHECK_HOST_CMD} "${_PSQL} -f -" <<SQL
22
with fk_indexes as (
33
select
4-
schemaname as schema_name,
5-
(indexrelid::regclass)::text as index_name,
6-
(relid::regclass)::text as table_name,
4+
n.nspname as schema_name,
5+
ci.relname as index_name,
6+
cr.relname as table_name,
77
(confrelid::regclass)::text as fk_table_ref,
88
array_to_string(indclass, ', ') as opclasses
9-
from
10-
pg_stat_user_indexes
11-
join pg_index using (indexrelid)
12-
left join pg_constraint
13-
on array_to_string(indkey, ',') = array_to_string(conkey, ',')
14-
and schemaname = (connamespace::regnamespace)::text
15-
and conrelid = relid
16-
and contype = 'f'
17-
where idx_scan = 0
18-
and indisunique is false
19-
and conkey is not null --conkey is not null then true else false end as is_fk_idx
9+
from pg_index i
10+
join pg_class ci on ci.oid = i.indexrelid and ci.relkind = 'i'
11+
join pg_class cr on cr.oid = i.indrelid and cr.relkind = 'r'
12+
join pg_namespace n on n.oid = ci.relnamespace
13+
join pg_constraint cn on cn.conrelid = cr.oid
14+
left join pg_stat_user_indexes si on si.indexrelid = i.indexrelid
15+
where
16+
contype = 'f'
17+
and i.indisunique is false
18+
and conkey is not null
19+
and ci.relpages > 100
20+
and si.idx_scan < 10
2021
), table_scans as (
2122
select relid,
2223
tables.idx_scan + tables.seq_scan as all_scans,
2324
( tables.n_tup_ins + tables.n_tup_upd + tables.n_tup_del ) as writes,
2425
pg_relation_size(relid) as table_size
2526
from pg_stat_user_tables as tables
27+
join pg_class c on c.oid = relid
28+
where c.relpages > 100
2629
), all_writes as (
2730
select sum(writes) as total_writes
2831
from table_scans
2932
), indexes as (
3033
select
31-
idx_stat.relid,
32-
idx_stat.indexrelid,
33-
idx_stat.schemaname as schema_name,
34-
idx_stat.relname as table_name,
35-
idx_stat.indexrelname as index_name,
36-
quote_ident(idx_stat.schemaname) as formated_schema_name,
37-
quote_ident(idx_stat.indexrelname) as formated_index_name,
38-
quote_ident(idx_stat.relname) as formated_table_name,
39-
coalesce(nullif(quote_ident(idx_stat.schemaname), 'public') || '.', '') || quote_ident(idx_stat.relname) as formated_relation_name,
40-
idx_stat.idx_scan,
41-
pg_relation_size(idx_stat.indexrelid) as index_bytes,
42-
indexdef ~* 'using btree' as idx_is_btree,
43-
pg_get_indexdef(pg_index.indexrelid) as index_def,
44-
array_to_string(pg_index.indclass, ', ') as opclasses
45-
from pg_stat_user_indexes as idx_stat
46-
join pg_index
47-
using (indexrelid)
48-
join pg_indexes as indexes
49-
on idx_stat.schemaname = indexes.schemaname
50-
and idx_stat.relname = indexes.tablename
51-
and idx_stat.indexrelname = indexes.indexname
34+
i.indrelid,
35+
i.indexrelid,
36+
n.nspname as schema_name,
37+
cr.relname as table_name,
38+
ci.relname as index_name,
39+
quote_ident(n.nspname) as formated_schema_name,
40+
quote_ident(ci.relname) as formated_index_name,
41+
quote_ident(cr.relname) as formated_table_name,
42+
coalesce(nullif(quote_ident(n.nspname), 'public') || '.', '') || quote_ident(cr.relname) as formated_relation_name,
43+
si.idx_scan,
44+
pg_relation_size(i.indexrelid) as index_bytes,
45+
ci.relpages,
46+
(case when a.amname = 'btree' then true else false end) as idx_is_btree,
47+
pg_get_indexdef(i.indexrelid) as index_def,
48+
array_to_string(i.indclass, ', ') as opclasses
49+
from pg_index i
50+
join pg_class ci on ci.oid = i.indexrelid and ci.relkind = 'i'
51+
join pg_class cr on cr.oid = i.indrelid and cr.relkind = 'r'
52+
join pg_namespace n on n.oid = ci.relnamespace
53+
join pg_am a ON ci.relam = a.oid
54+
left join pg_stat_user_indexes si on si.indexrelid = i.indexrelid
5255
where
53-
pg_index.indisunique = false
54-
and pg_index.indisvalid = true
56+
i.indisunique = false
57+
and i.indisvalid = true
58+
and ci.relpages > 100
5559
), index_ratios as (
5660
select
5761
i.indexrelid as index_id,
@@ -67,6 +71,7 @@ with fk_indexes as (
6771
as scans_per_write,
6872
index_bytes as index_size_bytes,
6973
table_size as table_size_bytes,
74+
i.relpages,
7075
idx_is_btree,
7176
index_def,
7277
formated_index_name,
@@ -78,9 +83,9 @@ with fk_indexes as (
7883
from indexes i
7984
left join fk_indexes fi on
8085
fi.fk_table_ref = i.table_name
86+
and fi.schema_name = i.schema_name
8187
and fi.opclasses like (i.opclasses || '%')
82-
join table_scans
83-
using (relid)
88+
join table_scans ts on ts.relid = i.indrelid
8489
),
8590
-- Never used indexes
8691
never_used_indexes as (
@@ -163,8 +168,9 @@ index_data as (
163168
*,
164169
indkey::text as columns,
165170
array_to_string(indclass, ', ') as opclasses
166-
from pg_index
167-
where indisvalid = true
171+
from pg_index i
172+
join pg_class ci on ci.oid = i.indexrelid and ci.relkind = 'i'
173+
where indisvalid = true and ci.relpages > 100
168174
), redundant_indexes as (
169175
select
170176
i2.indexrelid as index_id,
@@ -174,13 +180,14 @@ index_data as (
174180
irel.relname AS index_name,
175181
am1.amname as access_method,
176182
(i1.indexrelid::regclass)::text as reason,
183+
i1.indexrelid as reason_index_id,
177184
pg_get_indexdef(i1.indexrelid) main_index_def,
178185
pg_size_pretty(pg_relation_size(i1.indexrelid)) main_index_size,
179186
pg_get_indexdef(i2.indexrelid) index_def,
180187
pg_relation_size(i2.indexrelid) index_size_bytes,
181188
s.idx_scan as index_usage,
182189
quote_ident(tnsp.nspname) as formated_schema_name,
183-
quote_ident(irel.relname) as formated_index_name,
190+
coalesce(nullif(quote_ident(tnsp.nspname), 'public') || '.', '') || quote_ident(irel.relname) as formated_index_name,
184191
quote_ident(trel.relname) AS formated_table_name,
185192
coalesce(nullif(quote_ident(tnsp.nspname), 'public') || '.', '') || quote_ident(trel.relname) as formated_relation_name,
186193
i2.opclasses
@@ -218,6 +225,28 @@ index_data as (
218225
left join fk_indexes fi on
219226
fi.fk_table_ref = ri.table_name
220227
and fi.opclasses like (ri.opclasses || '%')
228+
),
229+
-- Cut recursive links
230+
redundant_indexes_tmp_num as (
231+
select row_number() over () num, rig.*
232+
from redundant_indexes_fk rig
233+
), redundant_indexes_tmp_links as (
234+
select
235+
ri1.*,
236+
ri2.num as r_num
237+
from redundant_indexes_tmp_num ri1
238+
left join redundant_indexes_tmp_num ri2 on ri2.reason_index_id = ri1.index_id and ri1.reason_index_id = ri2.index_id
239+
), redundant_indexes_tmp_cut as (
240+
select
241+
*
242+
from redundant_indexes_tmp_links
243+
where num < r_num or r_num is null
244+
), redundant_indexes_cut_grouped as (
245+
select
246+
distinct(num),
247+
*
248+
from redundant_indexes_tmp_cut
249+
order by index_size_bytes desc
221250
), redundant_indexes_grouped as (
222251
select
223252
index_id,
@@ -237,7 +266,7 @@ index_data as (
237266
formated_table_name,
238267
formated_relation_name,
239268
supports_fk
240-
from redundant_indexes_fk
269+
from redundant_indexes_cut_grouped
241270
group by
242271
index_id,
243272
table_size_bytes,
@@ -258,8 +287,7 @@ index_data as (
258287
select row_number() over () num, rig.*
259288
from redundant_indexes_grouped rig
260289
limit ${ROWS_LIMIT}
261-
),
262-
redundant_indexes_json as (
290+
), redundant_indexes_json as (
263291
select
264292
json_object_agg(rin.schema_name || '.' || rin.index_name, rin) as json
265293
from redundant_indexes_num rin

0 commit comments

Comments
 (0)