awesome_explain 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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