awesome_explain 0.3.0 → 1.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.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/mongodb.yml +53 -0
  3. data/.github/workflows/postgres.yml +56 -0
  4. data/.gitignore +11 -0
  5. data/Appraisals +11 -0
  6. data/Gemfile.lock +209 -49
  7. data/LICENSE.txt +4 -20
  8. data/README.md +155 -7
  9. data/Rakefile +35 -1
  10. data/app/models/awesome_explain/application_record.rb +5 -0
  11. data/app/models/awesome_explain/controller.rb +20 -0
  12. data/app/models/awesome_explain/delayed_job.rb +7 -0
  13. data/app/models/awesome_explain/explain.rb +23 -0
  14. data/app/models/awesome_explain/log.rb +7 -0
  15. data/app/models/awesome_explain/pg_dml_stat.rb +4 -0
  16. data/app/models/awesome_explain/pg_seq_scan.rb +4 -0
  17. data/app/models/awesome_explain/plan_node.rb +52 -0
  18. data/app/models/awesome_explain/plan_tree.rb +66 -0
  19. data/app/models/awesome_explain/sidekiq_worker.rb +7 -0
  20. data/app/models/awesome_explain/sql_explain.rb +14 -0
  21. data/app/models/awesome_explain/sql_plan_node.rb +73 -0
  22. data/app/models/awesome_explain/sql_plan_stats.rb +34 -0
  23. data/app/models/awesome_explain/sql_plan_tree.rb +133 -0
  24. data/app/models/awesome_explain/sql_query.rb +7 -0
  25. data/app/models/awesome_explain/stacktrace.rb +11 -0
  26. data/awesome_explain.gemspec +16 -5
  27. data/bin/rails +14 -0
  28. data/data/mongodb/customers.bson +0 -0
  29. data/data/mongodb/customers.metadata.json +1 -0
  30. data/data/mongodb/line_items.bson +0 -0
  31. data/data/mongodb/line_items.metadata.json +1 -0
  32. data/data/mongodb/orders.bson +0 -0
  33. data/data/mongodb/orders.metadata.json +1 -0
  34. data/data/mongodb/products.bson +0 -0
  35. data/data/mongodb/products.metadata.json +1 -0
  36. data/data/postgresql/dvdrental.tar +0 -0
  37. data/db/migrate/20200507214801_stacktraces.rb +12 -0
  38. data/db/migrate/20200507214949_controllers.rb +16 -0
  39. data/db/migrate/20200507215205_logs.rb +22 -0
  40. data/db/migrate/20200507215243_explains.rb +27 -0
  41. data/gemfiles/rails_4.gemfile +7 -0
  42. data/gemfiles/rails_4.gemfile.lock +208 -0
  43. data/gemfiles/rails_5.gemfile +7 -0
  44. data/gemfiles/rails_5.gemfile.lock +209 -0
  45. data/gemfiles/rails_6.gemfile +7 -0
  46. data/gemfiles/rails_6.gemfile.lock +233 -0
  47. data/images/universe.png +0 -0
  48. data/lib/awesome_explain.rb +79 -2
  49. data/lib/awesome_explain/config.rb +196 -0
  50. data/lib/awesome_explain/engine.rb +5 -0
  51. data/lib/awesome_explain/insights/active_record_insights.rb +137 -0
  52. data/lib/awesome_explain/insights/base.rb +18 -0
  53. data/lib/awesome_explain/insights/mongoid_insights.rb +44 -0
  54. data/lib/awesome_explain/insights/sql_plans_insights.rb +64 -0
  55. data/lib/awesome_explain/kernel.rb +17 -0
  56. data/lib/awesome_explain/mongodb/base.rb +4 -0
  57. data/lib/awesome_explain/mongodb/command_start.rb +84 -0
  58. data/lib/awesome_explain/mongodb/command_success.rb +58 -0
  59. data/lib/awesome_explain/mongodb/formatter.rb +62 -0
  60. data/lib/awesome_explain/mongodb/helpers.rb +71 -0
  61. data/lib/awesome_explain/queue/command.rb +17 -0
  62. data/lib/awesome_explain/queue/simple_queue.rb +88 -0
  63. data/lib/awesome_explain/renderers/active_record.rb +114 -0
  64. data/lib/awesome_explain/renderers/base.rb +2 -0
  65. data/lib/awesome_explain/renderers/mongoid.rb +20 -33
  66. data/lib/awesome_explain/sidekiq_middleware.rb +17 -0
  67. data/lib/awesome_explain/stats/postgresql.rb +54 -0
  68. data/lib/awesome_explain/subscribers/active_record_passive_subscriber.rb +82 -0
  69. data/lib/awesome_explain/subscribers/active_record_subscriber.rb +187 -0
  70. data/lib/awesome_explain/subscribers/base.rb +3 -0
  71. data/lib/awesome_explain/subscribers/command_subscriber.rb +53 -0
  72. data/lib/awesome_explain/tasks/db.rb +325 -0
  73. data/lib/awesome_explain/utils/color.rb +16 -0
  74. data/lib/awesome_explain/version.rb +1 -1
  75. data/lib/tasks/ae.rake +28 -0
  76. data/lib/tasks/awesome_explain_tasks.rake +4 -0
  77. metadata +242 -25
  78. data/.travis.yml +0 -19
@@ -0,0 +1,187 @@
1
+ module AwesomeExplain::Subscribers
2
+ class ActiveRecordSubscriber < ActiveSupport::LogSubscriber
3
+ def sql(event)
4
+ if track_sql(event) && db_logging_enbled?
5
+ sql = event.payload[:sql]
6
+ begin
7
+ table_name_and_schema = extract_table_name_and_schema(sql)
8
+ table_name = table_name_and_schema.first
9
+ schema_name = table_name_and_schema.last
10
+ request_id = event.payload[:connection_id]
11
+ binds = event.payload[:binds]
12
+ cached = event.payload[:name] == 'CACHE'
13
+ operation = extract_sql_operation(sql)
14
+ name = event.payload[:name]
15
+ stacktrace = caller
16
+
17
+ ActiveRecord::Base.transaction do
18
+ explain = nil
19
+ if db_explain_enabled?(sql)# && !cached
20
+ connection_id = event.payload[:connection_id]
21
+ connection = ::AwesomeExplain::Config.instance.connection
22
+
23
+ explain_uuid = SecureRandom.uuid
24
+ explain = connection.raw_connection.exec("EXPLAIN (ANALYZE true, COSTS true, FORMAT json) #{sql}")
25
+ #explain = connection.raw_connection.exec_prepared("ae_#{explain_uuid}", binds).to_a
26
+ explain = explain.map { |h| h.values.first }.join("\n")
27
+
28
+ explain = ::AwesomeExplain::SqlExplain.new({
29
+ explain_output: explain,
30
+ stacktrace_id: resolve_stracktrace_id(stacktrace),
31
+ controller_id: resolve_controller_id,
32
+ })
33
+ explain.save
34
+ end
35
+
36
+ sql_query = {
37
+ table_name: table_name,
38
+ schema_name: schema_name,
39
+ app_name: ::AwesomeExplain::Config.instance.app_name,
40
+ source_name: resolve_source_name,
41
+ duration: event.duration,
42
+ query: sql,
43
+ binds: binds.to_json,
44
+ cached: cached,
45
+ name: name,
46
+ operation: operation,
47
+ session_id: Thread.current[:ae_session_id],
48
+ stacktrace_id: resolve_stracktrace_id(stacktrace),
49
+ sql_explain_id: explain&.id,
50
+ controller_id: resolve_controller_id,
51
+ sidekiq_worker_id: resolve_sidekiq_class_id,
52
+ delayed_job_id: resolve_delayed_job_class_id,
53
+ }
54
+ ::AwesomeExplain::SqlQuery.create(sql_query)
55
+ end
56
+ rescue => exception
57
+ logger.warn sql
58
+ logger.warn exception.to_s
59
+ logger.warn exception.backtrace[0..5]
60
+ end
61
+ end
62
+ end
63
+
64
+ def track_sql(event)
65
+ return false if event.payload[:connection].class.name == 'ActiveRecord::ConnectionAdapters::SQLite3Adapter'
66
+ sql = event.payload[:sql]
67
+ !sql.match(/EXPLAIN|SAVEPOINT|nextval|CREATE|BEGIN|COMMIT|ROLLBACK|begin|commit|rollback|ar_|sql_|pg_|explain|logs|controllers|stacktraces|schema_migrations|delayed_jobs/) &&
68
+ sql.strip == sql &&
69
+ event.payload[:name] != 'SCHEMA'
70
+ end
71
+
72
+ def ddm_query?(sql)
73
+ matched = sql.match(/INSERT|DELETE|UPDATE/)
74
+ matched.present? && matched[0].present?
75
+ end
76
+
77
+ def resolve_source_name
78
+ Thread.current['ae_source'] || DEFAULT_SOURCE_NAME
79
+ end
80
+
81
+ def controller_data
82
+ Thread.current['ae_controller_data']
83
+ end
84
+
85
+ def extract_sidekiq_jid(args)
86
+ Thread.current[:sidekiq_job].dig('jid')
87
+ end
88
+
89
+ def extract_delayed_job_jid(args)
90
+ Thread.current[:delayed_job]&.id
91
+ end
92
+
93
+ def resolve_stracktrace_id(stacktrace)
94
+ stacktrace_str = stacktrace
95
+ .select {|c| c =~ /^#{::AwesomeExplain::Config.instance.rails_path + '\/(lib|app|db)\/'}/ }
96
+ .map {|c| c.gsub Rails.root.to_s, ''}
97
+ .to_json
98
+
99
+ stacktrace = ::AwesomeExplain::Stacktrace.find_or_create_by({
100
+ stacktrace: stacktrace_str
101
+ })
102
+
103
+ stacktrace.id
104
+ end
105
+
106
+ def resolve_controller_id
107
+ data = controller_data
108
+ return nil unless data.present?
109
+ ::AwesomeExplain::Controller.find_or_create_by({
110
+ name: controller_data[:controller],
111
+ action: controller_data[:action],
112
+ path: controller_data[:path],
113
+ params: controller_data[:params].to_json,
114
+ session_id: Thread.current['ae_session_id']
115
+ }).id
116
+ end
117
+
118
+ def resolve_sidekiq_class_id
119
+ return unless Thread.current[:sidekiq_worker_class].present?
120
+ sidekiq_worker_class_str = Thread.current[:sidekiq_worker_class]
121
+ sidekiq_queue_str = Thread.current[:sidekiq_queue].to_s
122
+ sidekiq_worker = ::AwesomeExplain::SidekiqWorker.find_or_create_by({
123
+ worker: sidekiq_worker_class_str,
124
+ queue: sidekiq_queue_str,
125
+ jid: extract_sidekiq_jid(Thread.current[:sidekiq_job]),
126
+ params: Thread.current[:sidekiq_job].present? ? Thread.current[:sidekiq_job].to_json : {}
127
+ })
128
+
129
+ sidekiq_worker.id
130
+ end
131
+
132
+ def resolve_delayed_job_class_id
133
+ return unless Thread.current[:delayed_worker_class].present?
134
+ delayed_worker_class_str = Thread.current[:delayed_worker_class]
135
+ delayed_job_queue_str = Thread.current[:delayed_job_queue].to_s
136
+ delayed_job_worker = ::AwesomeExplain::DelayedJob.find_or_create_by({
137
+ job: delayed_worker_class_str,
138
+ jid: extract_delayed_job_jid(Thread.current[:delayed_job]),
139
+ params: Thread.current[:delayed_job].present? ? Thread.current[:delayed_job].to_json : {}
140
+ })
141
+
142
+ delayed_job_worker.id
143
+ end
144
+
145
+ def db_explain_enabled?(sql)
146
+ # return true if Thread.current['ae_analyze']
147
+ # return false if Rails.const_defined?('Console')
148
+ return false if ddm_query?(sql)
149
+ true
150
+ end
151
+
152
+ def db_logging_enbled?
153
+ # return true if Thread.current['ae_analyze']
154
+ return false if Rails.const_defined?('Console')
155
+ true
156
+ end
157
+
158
+ def extract_sql_operation(sql)
159
+ sql.match(/SELECT|INSERT|DELETE|UPDATE/)[0]
160
+ end
161
+
162
+ def extract_table_name_and_schema(sql)
163
+ matched = sql.match(/FROM\s+(\"\w+\")\.?(\"\w+\")?/)
164
+ return reduce_table_and_schema(matched) if matched && matched[1].present?
165
+
166
+ matched = sql.match(/INSERT INTO\s+(\"\w+\")\.?(\"\w+\")?/)
167
+ return reduce_table_and_schema(matched) if matched && matched[1].present?
168
+
169
+ matched = sql.match(/UPDATE\s+(\"\w+\")\.?(\"\w+\")?/)
170
+ return reduce_table_and_schema(matched) if matched && matched[1].present?
171
+
172
+ # matched = sql.match(/DELETE FROM\s+(\"\w+\")\.?(\"\w+\")?/)
173
+ # return reduce_table_and_schema(matched) if matched && matched[1].present?
174
+ end
175
+
176
+ def reduce_table_and_schema(matched)
177
+ if matched[1].present?
178
+ table_name = matched[2].nil? ? matched[1] : matched[2]
179
+ table_name = table_name.gsub(/\"/, '')
180
+ schema_name = matched[2].nil? ? 'public' : matched[1]
181
+ schema_name = schema_name.gsub(/\"/, '')
182
+
183
+ return [table_name, schema_name]
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,3 @@
1
+ require 'awesome_explain/subscribers/command_subscriber'
2
+ require 'awesome_explain/subscribers/active_record_subscriber'
3
+ require 'awesome_explain/subscribers/active_record_passive_subscriber'
@@ -0,0 +1,53 @@
1
+ module AwesomeExplain::Subscribers
2
+ class CommandSubscriber
3
+ include AwesomeExplain::Mongodb::Formatter
4
+ include AwesomeExplain::Mongodb::Helpers
5
+ include AwesomeExplain::Mongodb::CommandStart
6
+ include AwesomeExplain::Mongodb::CommandSuccess
7
+
8
+ attr_reader :logger
9
+ attr_accessor :options, :queries, :stats
10
+
11
+ def initialize(options = {})
12
+ init(options)
13
+ @logger = ::AwesomeExplain::Config.instance.logger
14
+ end
15
+
16
+ def started(event)
17
+ unless COMMAND_NAMES_BLACKLIST.include?(event.command_name)
18
+ handle_command_start(event)
19
+ end
20
+ end
21
+
22
+ def succeeded(event)
23
+ unless COMMAND_NAMES_BLACKLIST.include?(event.command_name)
24
+ handle_command_success(event)
25
+ end
26
+ end
27
+
28
+ def failed(event)
29
+ end
30
+
31
+ def get(metric)
32
+ case metric
33
+ when :total_performed_queries
34
+ total_performed_queries
35
+ end
36
+ end
37
+
38
+ def clear
39
+ init
40
+ end
41
+
42
+ private
43
+ def init(options = {})
44
+ @options = options
45
+ @queries = Hash.new({})
46
+ @stats = {
47
+ total_duration: 0,
48
+ collections: {},
49
+ performed_queries: QUERIES.inject(Hash.new) {|h, q| h[q] = 0; h}
50
+ }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,325 @@
1
+ require 'pg'
2
+
3
+ module AwesomeExplain
4
+ module Tasks
5
+ class DB
6
+ def self.build
7
+ ActiveRecord::Base.establish_connection(
8
+ AwesomeExplain::Config.instance.db_config
9
+ )
10
+
11
+ adapter = AwesomeExplain::Config.instance.adapter&.to_sym
12
+ adapter == :postgres ? build_postgres_db : build_sqlite3_db
13
+ end
14
+
15
+ def self.drop_postgres_db
16
+ conn = PG.connect(
17
+ dbname: 'postgres',
18
+ host: postgres_host,
19
+ user: postgres_username,
20
+ password: postgres_password
21
+ )
22
+
23
+ conn.exec("DROP DATABASE #{AwesomeExplain::Config::POSTGRES_DEV_DBNAME}")
24
+ end
25
+
26
+ def self.build_postgres_db
27
+ conn = PG.connect(
28
+ dbname: 'postgres',
29
+ host: postgres_host,
30
+ user: postgres_username,
31
+ password: postgres_password
32
+ )
33
+ sql = <<-SQL
34
+ select exists(
35
+ SELECT datname FROM pg_catalog.pg_database WHERE lower(datname) = lower('#{AwesomeExplain::Config::POSTGRES_DEV_DBNAME}')
36
+ );
37
+ SQL
38
+
39
+ if conn.exec(sql).to_a.first.dig('exists') == 'f'
40
+ conn.exec("CREATE DATABASE #{AwesomeExplain::Config::POSTGRES_DEV_DBNAME}")
41
+ end
42
+
43
+ connection = ActiveRecord::Base.establish_connection(
44
+ AwesomeExplain::Config.instance.db_config
45
+ ).connection
46
+
47
+ ActiveRecord::Schema.define do
48
+ unless connection.table_exists?(:stacktraces)
49
+ connection.create_table :stacktraces do |t|
50
+ t.column :stacktrace, :string
51
+ t.timestamps
52
+ end
53
+ end
54
+
55
+ unless connection.table_exists?(:sidekiq_workers)
56
+ connection.create_table :sidekiq_workers do |t|
57
+ t.column :worker, :string
58
+ t.column :queue, :string
59
+ t.column :jid, :string
60
+ t.column :params, :string
61
+ t.timestamps
62
+ end
63
+ end
64
+
65
+ unless connection.table_exists?(:delayed_jobs)
66
+ connection.create_table :delayed_jobs do |t|
67
+ t.column :job, :string
68
+ t.column :queue, :string
69
+ t.column :jid, :string
70
+ t.column :params, :string
71
+ t.timestamps
72
+ end
73
+ end
74
+
75
+ unless connection.table_exists?(:controllers)
76
+ connection.create_table :controllers do |t|
77
+ t.column :name, :string
78
+ t.column :action, :string
79
+ t.column :path, :string
80
+ t.column :params, :string
81
+ t.column :session_id, :string
82
+ t.timestamps
83
+ end
84
+ end
85
+
86
+ unless connection.table_exists?(:logs)
87
+ connection.create_table :logs do |t|
88
+ t.column :collection, :string
89
+ t.column :app_name, :string
90
+ t.column :source_name, :string
91
+ t.column :operation, :string
92
+ t.column :collscan, :integer
93
+ t.column :command, :string
94
+ t.decimal :duration, precision: 8, scale: 2
95
+ t.column :session_id, :string
96
+ t.column :lsid, :string
97
+
98
+ t.column :sidekiq_args, :string
99
+ t.column :stacktrace_id, :integer
100
+ t.column :explain_id, :integer
101
+ t.column :controller_id, :integer
102
+ t.column :sidekiq_worker_id, :integer
103
+ t.timestamps
104
+ end
105
+ end
106
+
107
+ unless connection.table_exists?(:sql_queries)
108
+ connection.create_table :sql_queries do |t|
109
+ t.column :table_name, :string
110
+ t.column :schema_name, :string
111
+ t.column :app_name, :string
112
+ t.column :source_name, :string
113
+ t.column :operation, :string
114
+ t.column :query, :string
115
+ t.column :binds, :string
116
+ t.decimal :duration, precision: 8, scale: 2
117
+ t.column :session_id, :string
118
+ t.column :cached, :integer
119
+ t.column :name, :string
120
+
121
+ t.column :sidekiq_args, :string
122
+ t.column :stacktrace_id, :integer
123
+ t.column :sql_explain_id, :integer
124
+ t.column :controller_id, :integer
125
+ t.column :sidekiq_worker_id, :integer
126
+ t.column :delayed_job_id, :integer
127
+ t.timestamps
128
+ end
129
+ end
130
+
131
+ unless connection.table_exists?(:explains)
132
+ connection.create_table :explains do |t|
133
+ t.column :collection, :string
134
+ t.column :source_name, :string
135
+ t.column :command, :string
136
+ t.column :collscan, :integer
137
+ t.column :winning_plan, :string
138
+ t.column :winning_plan_raw, :string
139
+ t.column :used_indexes, :string
140
+ t.decimal :duration, precision: 8, scale: 2
141
+ t.column :documents_returned, :integer
142
+ t.column :documents_examined, :integer
143
+ t.column :keys_examined, :integer
144
+ t.column :rejected_plans, :integer
145
+ t.column :session_id, :string
146
+ t.column :lsid, :string
147
+ t.column :stacktrace_id, :integer
148
+ t.column :controller_id, :integer
149
+ t.timestamps
150
+ end
151
+ end
152
+
153
+ unless connection.table_exists?(:sql_explains)
154
+ connection.create_table :sql_explains do |t|
155
+ t.column :explain_output, :string
156
+ t.column :stacktrace_id, :integer
157
+ t.column :controller_id, :integer
158
+ t.timestamps
159
+ end
160
+ end
161
+
162
+ unless connection.table_exists?(:transactions)
163
+ connection.create_table :transactions do |t|
164
+ t.column :params, :string
165
+ t.column :format, :string
166
+ t.column :method, :string
167
+ t.column :ip, :string
168
+ t.column :stash, :string
169
+ t.column :status, :string
170
+ t.column :view_runtime, :string
171
+
172
+ t.column :controller_id, :integer
173
+ t.timestamps
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ def self.build_sqlite3_db
180
+ ActiveRecord::Schema.define do
181
+ unless connection.table_exists?(:stacktraces)
182
+ connection.create_table :stacktraces do |t|
183
+ t.column :stacktrace, :string
184
+ t.timestamps
185
+ end
186
+ end
187
+
188
+ unless connection.table_exists?(:sidekiq_workers)
189
+ connection.create_table :sidekiq_workers do |t|
190
+ t.column :worker, :string
191
+ t.column :queue, :string
192
+ t.column :jid, :string
193
+ t.column :params, :string
194
+ t.timestamps
195
+ end
196
+ end
197
+
198
+ unless connection.table_exists?(:delayed_jobs)
199
+ connection.create_table :delayed_jobs do |t|
200
+ t.column :job, :string
201
+ t.column :queue, :string
202
+ t.column :jid, :string
203
+ t.column :params, :string
204
+ t.timestamps
205
+ end
206
+ end
207
+
208
+ unless connection.table_exists?(:controllers)
209
+ connection.create_table :controllers do |t|
210
+ t.column :name, :string
211
+ t.column :action, :string
212
+ t.column :path, :string
213
+ t.column :params, :string
214
+ t.column :session_id, :string
215
+ t.timestamps
216
+ end
217
+ end
218
+
219
+ unless connection.table_exists?(:logs)
220
+ connection.create_table :logs do |t|
221
+ t.column :collection, :string
222
+ t.column :app_name, :string
223
+ t.column :source_name, :string
224
+ t.column :operation, :string
225
+ t.column :collscan, :integer
226
+ t.column :command, :string
227
+ t.column :duration, :double
228
+ t.column :session_id, :string
229
+ t.column :lsid, :string
230
+
231
+ t.column :sidekiq_args, :string
232
+ t.column :stacktrace_id, :integer
233
+ t.column :explain_id, :integer
234
+ t.column :controller_id, :integer
235
+ t.column :sidekiq_worker_id, :integer
236
+ t.timestamps
237
+ end
238
+ end
239
+
240
+ unless connection.table_exists?(:sql_queries)
241
+ connection.create_table :sql_queries do |t|
242
+ t.column :table_name, :string
243
+ t.column :schema_name, :string
244
+ t.column :app_name, :string
245
+ t.column :source_name, :string
246
+ t.column :operation, :string
247
+ t.column :query, :string
248
+ t.column :binds, :string
249
+ t.column :duration, :double
250
+ t.column :session_id, :string
251
+ t.column :cached, :integer
252
+ t.column :name, :string
253
+
254
+ t.column :sidekiq_args, :string
255
+ t.column :stacktrace_id, :integer
256
+ t.column :sql_explain_id, :integer
257
+ t.column :controller_id, :integer
258
+ t.column :sidekiq_worker_id, :integer
259
+ t.column :delayed_job_id, :integer
260
+ t.timestamps
261
+ end
262
+ end
263
+
264
+ unless connection.table_exists?(:explains)
265
+ connection.create_table :explains do |t|
266
+ t.column :collection, :string
267
+ t.column :source_name, :string
268
+ t.column :command, :string
269
+ t.column :collscan, :integer
270
+ t.column :winning_plan, :string
271
+ t.column :winning_plan_raw, :string
272
+ t.column :used_indexes, :string
273
+ t.column :duration, :double
274
+ t.column :documents_returned, :integer
275
+ t.column :documents_examined, :integer
276
+ t.column :keys_examined, :integer
277
+ t.column :rejected_plans, :integer
278
+ t.column :session_id, :string
279
+ t.column :lsid, :string
280
+ t.column :stacktrace_id, :integer
281
+ t.column :controller_id, :integer
282
+ t.timestamps
283
+ end
284
+ end
285
+
286
+ unless connection.table_exists?(:sql_explains)
287
+ connection.create_table :sql_explains do |t|
288
+ t.column :explain_output, :string
289
+ t.column :stacktrace_id, :integer
290
+ t.column :controller_id, :integer
291
+ t.timestamps
292
+ end
293
+ end
294
+
295
+ unless connection.table_exists?(:transactions)
296
+ connection.create_table :transactions do |t|
297
+ t.column :params, :string
298
+ t.column :format, :string
299
+ t.column :method, :string
300
+ t.column :ip, :string
301
+ t.column :stash, :string
302
+ t.column :status, :string
303
+ t.column :view_runtime, :string
304
+
305
+ t.column :controller_id, :integer
306
+ t.timestamps
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ def self.postgres_host
313
+ AwesomeExplain::Config.instance.postgres_host
314
+ end
315
+
316
+ def self.postgres_username
317
+ AwesomeExplain::Config.instance.postgres_username
318
+ end
319
+
320
+ def self.postgres_password
321
+ AwesomeExplain::Config.instance.postgres_password
322
+ end
323
+ end
324
+ end
325
+ end