Skip to content

Commit

Permalink
Improved logic for covering indexes
Browse files Browse the repository at this point in the history
  • Loading branch information
ankane committed Nov 24, 2020
1 parent 78ed874 commit 593d240
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 5 deletions.
15 changes: 10 additions & 5 deletions lib/pghero/methods/suggested_indexes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,17 @@ def suggested_indexes_by_query(queries: nil, query_stats: nil, indexes: nil)
# indexes of same type
indexes = existing_columns[index[:using] || "btree"][index[:table]]

# gist indexes without an opclass
# (opclass is part of column name, so columns won't match if opclass present)
indexes += existing_columns["gist"][index[:table]]
if best_index[:structure][:sort].empty?
# gist indexes without an opclass
# (opclass is part of column name, so columns won't match if opclass present)
indexes += existing_columns["gist"][index[:table]]

# hash indexes work for equality
indexes += existing_columns["hash"][index[:table]] if best_index[:structure][:where].all? { |v| v[:op] == "=" }
# hash indexes work for equality
indexes += existing_columns["hash"][index[:table]] if best_index[:structure][:where].all? { |v| v[:op] == "=" }

# brin indexes work for all
indexes += existing_columns["brin"][index[:table]]
end

covering_index = indexes.find { |e| index_covers?(e, index[:columns]) }
if covering_index
Expand Down
42 changes: 42 additions & 0 deletions test/suggested_indexes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,30 @@ def test_basic
assert_equal [{table: "users", columns: ["email"]}], database.suggested_indexes.map { |q| q.except(:queries, :details) }
end

def test_primary_key
query = "SELECT * FROM users WHERE id = 1"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_equal ["id"], result[:covering_index]
end

def test_hash
query = "SELECT * FROM users WHERE login_attempts = 1"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_equal ["login_attempts"], result[:covering_index]
end

def test_hash_multiple_values
query = "SELECT * FROM users WHERE login_attempts IN (1, 2)"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_equal ["login_attempts"], result[:covering_index]
end

def test_hash_greater_than
query = "SELECT * FROM users WHERE login_attempts > 1"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_nil result[:covering_index]
end

def test_existing_index
User.where("updated_at > ?", Time.now).to_a
assert_equal [], database.suggested_indexes.map { |q| q.except(:queries, :details) }
Expand All @@ -36,4 +54,28 @@ def test_ltree
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_equal ["tree_path"], result[:covering_index]
end

def test_range
query = "SELECT * FROM users WHERE range = '[0, 0]'"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_equal ["range"], result[:covering_index]
end

def test_brin
query = "SELECT * FROM users WHERE created_at = NOW()"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_equal ["created_at"], result[:covering_index]
end

def test_brin_order
query = "SELECT * FROM users ORDER BY created_at LIMIT 1"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_nil result[:covering_index]
end

def test_gin
query = "SELECT * FROM users WHERE metadata = '{}'::jsonb"
result = database.suggested_indexes_by_query(queries: [query])[query]
assert_nil result[:covering_index]
end
end
5 changes: 5 additions & 0 deletions test/support/migrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,16 @@
t.boolean :active
t.string :country
t.column :tree_path, :ltree
t.column :range, :int4range
t.column :metadata, :jsonb
t.timestamp :created_at
t.timestamp :updated_at
t.index :id # duplicate index
t.index :updated_at
t.index :login_attempts, using: :hash
t.index "country gist_trgm_ops", using: :gist
t.index :tree_path, using: :gist
t.index :range, using: :gist
t.index :created_at, using: :brin
t.index :metadata, using: :gin
end
2 changes: 2 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class User < ActiveRecord::Base
active: true,
country: "Test #{rand(30)}",
tree_path: "path#{rand(30)}",
range: (0..rand(5)),
metadata: {favorite_color: "red"},
created_at: Time.now - rand(50).days,
updated_at: Time.now - rand(50).days
}
Expand Down

0 comments on commit 593d240

Please sign in to comment.