pghero 1.1.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pghero might be problematic. Click here for more details.

@@ -5,4 +5,9 @@ namespace :pghero do
5
5
  task capture_query_stats: :environment do
6
6
  PgHero.capture_query_stats
7
7
  end
8
+
9
+ desc "autoindex"
10
+ task autoindex: :environment do
11
+ PgHero.autoindex_all(create: true)
12
+ end
8
13
  end
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "1.1.4"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -0,0 +1,131 @@
1
+ require_relative "test_helper"
2
+
3
+ class BestIndexTest < Minitest::Test
4
+ def test_where
5
+ assert_best_index ({table: "users", columns: ["city_id"]}), "SELECT * FROM users WHERE city_id = 1"
6
+ end
7
+
8
+ def test_all_values
9
+ index = PgHero.best_index("SELECT * FROM users WHERE login_attempts = 1 ORDER BY created_at")
10
+ expected = {
11
+ found: true,
12
+ structure: {table: "users", where: [{column: "login_attempts", op: "="}], sort: [{column: "created_at", direction: "asc"}]},
13
+ index: {table: "users", columns: ["login_attempts", "created_at"]},
14
+ rows: 10000,
15
+ row_estimates: {"login_attempts" => 333, "created_at" => 1},
16
+ row_progression: [10000, 333, 0]
17
+ }
18
+ assert_equal expected, index
19
+ end
20
+
21
+ def test_where_multiple_columns
22
+ assert_best_index ({table: "users", columns: ["city_id", "login_attempts"]}), "SELECT * FROM users WHERE city_id = 1 and login_attempts = 2"
23
+ end
24
+
25
+ def test_where_unique
26
+ assert_best_index ({table: "users", columns: ["email"]}), "SELECT * FROM users WHERE city_id = 1 AND email = 'person2@example.org'"
27
+ end
28
+
29
+ def test_order
30
+ assert_best_index ({table: "users", columns: ["created_at"]}), "SELECT * FROM users ORDER BY created_at"
31
+ end
32
+
33
+ def test_order_multiple
34
+ assert_best_index ({table: "users", columns: ["login_attempts", "created_at"]}), "SELECT * FROM users ORDER BY login_attempts, created_at"
35
+ end
36
+
37
+ def test_order_multiple_direction
38
+ assert_best_index ({table: "users", columns: ["login_attempts"]}), "SELECT * FROM users ORDER BY login_attempts DESC, created_at"
39
+ end
40
+
41
+ def test_order_multiple_unique
42
+ assert_best_index ({table: "users", columns: ["id"]}), "SELECT * FROM users ORDER BY id, created_at"
43
+ end
44
+
45
+ def test_where_unique_order
46
+ assert_best_index ({table: "users", columns: ["email"]}), "SELECT * FROM users WHERE email = 'person2@example.org' ORDER BY created_at"
47
+ end
48
+
49
+ def test_where_order
50
+ assert_best_index ({table: "users", columns: ["login_attempts", "created_at"]}), "SELECT * FROM users WHERE login_attempts = 1 ORDER BY created_at"
51
+ end
52
+
53
+ def test_where_in
54
+ assert_best_index ({table: "users", columns: ["city_id"]}), "SELECT * FROM users WHERE city_id IN (1, 2)"
55
+ end
56
+
57
+ def test_between
58
+ skip
59
+ assert_best_index ({table: "users", columns: ["city_id"]}), "SELECT * FROM users WHERE city_id BETWEEN 1 AND 2"
60
+ end
61
+
62
+ def test_where_prepared
63
+ assert_best_index ({table: "users", columns: ["city_id"]}), "SELECT * FROM users WHERE city_id = $1"
64
+ end
65
+
66
+ def test_where_normalized
67
+ assert_best_index ({table: "users", columns: ["city_id"]}), "SELECT * FROM users WHERE city_id = ?"
68
+ end
69
+
70
+ def test_is_null
71
+ assert_best_index ({table: "users", columns: ["zip_code"]}), "SELECT * FROM users WHERE zip_code IS NULL"
72
+ end
73
+
74
+ def test_is_null_equal
75
+ assert_best_index ({table: "users", columns: ["zip_code", "login_attempts"]}), "SELECT * FROM users WHERE zip_code IS NULL AND login_attempts = ?"
76
+ end
77
+
78
+ def test_is_not_null
79
+ assert_best_index ({table: "users", columns: ["login_attempts"]}), "SELECT * FROM users WHERE zip_code IS NOT NULL AND login_attempts = ?"
80
+ end
81
+
82
+ def test_update
83
+ assert_best_index ({table: "users", columns: ["city_id"]}), "UPDATE users SET email = 'test' WHERE city_id = 1"
84
+ end
85
+
86
+ def test_delete
87
+ assert_best_index ({table: "users", columns: ["city_id"]}), "DELETE FROM users WHERE city_id = 1"
88
+ end
89
+
90
+ def test_parse_error
91
+ assert_no_index "Parse error", "SELECT *123'"
92
+ end
93
+
94
+ def test_multiple_tables
95
+ assert_no_index "Unknown structure", "SELECT * FROM users INNER JOIN cities ON cities.id = users.city_id"
96
+ end
97
+
98
+ def test_no_columns
99
+ assert_no_index "No columns to index", "SELECT * FROM users"
100
+ end
101
+
102
+ def test_small_table
103
+ assert_no_index "No index needed if less than 500 rows", "SELECT * FROM states WHERE name = 'State 1'"
104
+ end
105
+
106
+ def test_system_table
107
+ assert_no_index "System table", "SELECT COUNT(*) AS count FROM pg_extension WHERE extname = ?"
108
+ end
109
+
110
+ def test_insert
111
+ assert_no_index "INSERT statement", "INSERT INTO users (login_attempts) VALUES (1)"
112
+ end
113
+
114
+ def test_set
115
+ assert_no_index "SET statement", "set client_encoding to 'UTF8'"
116
+ end
117
+
118
+ protected
119
+
120
+ def assert_best_index(expected, statement)
121
+ index = PgHero.best_index(statement)
122
+ assert index[:found]
123
+ assert_equal expected, index[:index]
124
+ end
125
+
126
+ def assert_no_index(explanation, statement)
127
+ index = PgHero.best_index(statement)
128
+ assert !index[:found]
129
+ assert_equal explanation, index[:explanation]
130
+ end
131
+ end
@@ -0,0 +1,18 @@
1
+ require_relative "test_helper"
2
+
3
+ class ExplainTest < Minitest::Test
4
+ def setup
5
+ City.delete_all
6
+ end
7
+
8
+ def test_explain
9
+ City.create!
10
+ PgHero.explain("ANALYZE DELETE FROM cities")
11
+ assert_equal 1, City.count
12
+ end
13
+
14
+ def test_explain_multiple_statements
15
+ City.create!
16
+ assert_raises(ActiveRecord::StatementInvalid) { PgHero.explain("ANALYZE DELETE FROM cities; DELETE FROM cities; COMMIT") }
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "test_helper"
2
+
3
+ class SuggestedIndexesTest < Minitest::Test
4
+ def setup
5
+ PgHero.reset_query_stats
6
+ end
7
+
8
+ def test_basic
9
+ User.where(email: "person1@example.org").first
10
+ # User.where(email: "person1@example.org", city_id: 1).first
11
+ # User.where(city_id: 1).to_a
12
+ assert_equal [{table: "users", columns: ["email"]}], PgHero.suggested_indexes.map { |q| q.except(:queries) }
13
+ end
14
+ end
@@ -2,15 +2,57 @@ require "bundler/setup"
2
2
  Bundler.require(:default)
3
3
  require "minitest/autorun"
4
4
  require "minitest/pride"
5
+ require "pg_query"
5
6
 
6
7
  # for Minitest < 5
7
8
  Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
8
9
 
9
10
  ActiveRecord::Base.establish_connection adapter: "postgresql", database: "pghero_test"
10
11
 
11
- ActiveRecord::Migration.create_table :users, force: true do
12
- # just id
12
+ ActiveRecord::Migration.create_table :cities, force: true do |t|
13
+ t.string :name
14
+ end
15
+
16
+ class City < ActiveRecord::Base
17
+ end
18
+
19
+ class State < ActiveRecord::Base
13
20
  end
14
21
 
15
22
  class User < ActiveRecord::Base
16
23
  end
24
+
25
+ if ENV["SEED"]
26
+ ActiveRecord::Migration.create_table :users, force: true do |t|
27
+ t.integer :city_id
28
+ t.integer :login_attempts
29
+ t.string :email
30
+ t.string :zip_code
31
+ t.timestamp :created_at
32
+ end
33
+
34
+ User.transaction do
35
+ 10000.times do |i|
36
+ city_id = i % 100
37
+ User.create!(
38
+ city_id: city_id,
39
+ email: "person#{i}@example.org",
40
+ login_attempts: rand(30),
41
+ zip_code: i % 40 == 0 ? nil : "12345",
42
+ created_at: Time.now - rand(50).days
43
+ )
44
+ end
45
+ end
46
+ ActiveRecord::Base.connection.execute("VACUUM ANALYZE users")
47
+
48
+ ActiveRecord::Migration.create_table :states, force: true do |t|
49
+ t.string :name
50
+ end
51
+
52
+ State.transaction do
53
+ 50.times do |i|
54
+ State.create!(name: "State #{i}")
55
+ end
56
+ end
57
+ ActiveRecord::Base.connection.execute("VACUUM ANALYZE states")
58
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pghero
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-09 00:00:00.000000000 Z
11
+ date: 2015-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -99,11 +99,13 @@ files:
99
99
  - app/views/pg_hero/home/_live_queries_table.html.erb
100
100
  - app/views/pg_hero/home/_queries_table.html.erb
101
101
  - app/views/pg_hero/home/_query_stats_slider.html.erb
102
+ - app/views/pg_hero/home/_suggested_index.html.erb
102
103
  - app/views/pg_hero/home/connections.html.erb
103
104
  - app/views/pg_hero/home/explain.html.erb
104
105
  - app/views/pg_hero/home/index.html.erb
105
106
  - app/views/pg_hero/home/index_usage.html.erb
106
107
  - app/views/pg_hero/home/live_queries.html.erb
108
+ - app/views/pg_hero/home/maintenance.html.erb
107
109
  - app/views/pg_hero/home/queries.html.erb
108
110
  - app/views/pg_hero/home/space.html.erb
109
111
  - app/views/pg_hero/home/system.html.erb
@@ -114,6 +116,7 @@ files:
114
116
  - guides/Linux.md
115
117
  - guides/Query-Stats.md
116
118
  - guides/Rails.md
119
+ - guides/Suggested-Indexes.md
117
120
  - lib/generators/pghero/query_stats_generator.rb
118
121
  - lib/generators/pghero/templates/query_stats.rb
119
122
  - lib/pghero.rb
@@ -122,10 +125,12 @@ files:
122
125
  - lib/pghero/tasks.rb
123
126
  - lib/pghero/version.rb
124
127
  - pghero.gemspec
128
+ - test/best_index_test.rb
129
+ - test/explain_test.rb
125
130
  - test/gemfiles/activerecord31.gemfile
126
131
  - test/gemfiles/activerecord32.gemfile
127
132
  - test/gemfiles/activerecord40.gemfile
128
- - test/pghero_test.rb
133
+ - test/suggested_indexes_test.rb
129
134
  - test/test_helper.rb
130
135
  homepage: https://github.com/ankane/pghero
131
136
  licenses:
@@ -157,8 +162,11 @@ test_files:
157
162
  - guides/Linux.md
158
163
  - guides/Query-Stats.md
159
164
  - guides/Rails.md
165
+ - guides/Suggested-Indexes.md
166
+ - test/best_index_test.rb
167
+ - test/explain_test.rb
160
168
  - test/gemfiles/activerecord31.gemfile
161
169
  - test/gemfiles/activerecord32.gemfile
162
170
  - test/gemfiles/activerecord40.gemfile
163
- - test/pghero_test.rb
171
+ - test/suggested_indexes_test.rb
164
172
  - test/test_helper.rb
@@ -1,18 +0,0 @@
1
- require_relative "test_helper"
2
-
3
- class TestPgHero < Minitest::Test
4
- def setup
5
- User.delete_all
6
- end
7
-
8
- def test_explain
9
- User.create!
10
- PgHero.explain("ANALYZE DELETE FROM users")
11
- assert_equal 1, User.count
12
- end
13
-
14
- def test_explain_multiple_statements
15
- User.create!
16
- assert_raises(ActiveRecord::StatementInvalid) { PgHero.explain("ANALYZE DELETE FROM users; DELETE FROM users; COMMIT") }
17
- end
18
- end