Module: NewRelic::Agent::Database::ExplainPlanHelpers
- Included in:
- Statement
- Defined in:
- lib/new_relic/agent/database/explain_plan_helpers.rb
Constant Summary collapse
- SUPPORTED_ADAPTERS_FOR_EXPLAIN =
[:postgres, :mysql2, :mysql, :sqlite]
- SELECT =
'select'.freeze
- MULTIPLE_QUERIES =
SQL containing a semicolon in the middle (with something other than whitespace after it) may contain two or more queries. It’s not safe to EXPLAIN this kind of expression, since it could lead to executing unwanted SQL.
Regexp.new(';\s*\S+')
- QUERY_PLAN =
'QUERY PLAN'.freeze
- SQLITE_EXPLAIN_COLUMNS =
%w[addr opcode p1 p2 p3 p4 p5 comment]
Instance Method Summary collapse
- #handle_exception_in_explain ⇒ Object
- #is_select?(sql) ⇒ Boolean
- #multiple_queries?(sql) ⇒ Boolean
- #parameterized?(sql) ⇒ Boolean
- #process_explain_results_mysql(results) ⇒ Object
- #process_explain_results_mysql2(results) ⇒ Object
- #process_explain_results_postgres(results) ⇒ Object
- #process_explain_results_sqlite(results) ⇒ Object
- #process_resultset(results, adapter) ⇒ Object
-
#string_explain_plan_results(results) ⇒ Object
Sequel returns explain plans as just one big pre-formatted String In that case, we send a nil headers array, and the single string wrapped in an array for the values.
Instance Method Details
#handle_exception_in_explain ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 34 def handle_exception_in_explain yield rescue => e begin # guarantees no throw from explain_sql ::NewRelic::Agent.logger.error('Error getting query plan:', e) nil rescue # double exception. throw up your hands nil end end |
#is_select?(sql) ⇒ Boolean
15 16 17 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 15 def is_select?(sql) NewRelic::Agent::Database.parse_operation_from_query(sql) == SELECT end |
#multiple_queries?(sql) ⇒ Boolean
30 31 32 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 30 def multiple_queries?(sql) sql =~ MULTIPLE_QUERIES end |
#parameterized?(sql) ⇒ Boolean
19 20 21 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 19 def parameterized?(sql) Obfuscator.instance.obfuscate_single_quote_literals(sql) =~ /\$\d+/ end |
#process_explain_results_mysql(results) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 97 def process_explain_results_mysql(results) headers = [] values = [] if results.is_a?(Array) # We're probably using the jdbc-mysql gem for JRuby, which will give # us an array of hashes. headers = results.first.keys results.each do |row| values << headers.map { |h| row[h] } end else # We're probably using the native mysql driver gem, which will give us # a Mysql::Result object that responds to each_hash results.each_hash do |row| headers = row.keys values << headers.map { |h| row[h] } end end [headers, values] end |
#process_explain_results_mysql2(results) ⇒ Object
118 119 120 121 122 123 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 118 def process_explain_results_mysql2(results) headers = results.fields values = [] results.each { |row| values << row } [headers, values] end |
#process_explain_results_postgres(results) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 69 def process_explain_results_postgres(results) if defined?(::ActiveRecord::Result) && results.is_a?(::ActiveRecord::Result) query_plan_string = results.rows.join("\n") elsif results.is_a?(String) query_plan_string = results else lines = [] results.each { |row| lines << row[QUERY_PLAN] } query_plan_string = lines.join("\n") end unless NewRelic::Agent::Database.record_sql_method == :raw query_plan_string = NewRelic::Agent::Database::PostgresExplainObfuscator.obfuscate(query_plan_string) end values = query_plan_string.split("\n").map { |line| [line] } [[QUERY_PLAN], values] end |
#process_explain_results_sqlite(results) ⇒ Object
127 128 129 130 131 132 133 134 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 127 def process_explain_results_sqlite(results) headers = SQLITE_EXPLAIN_COLUMNS values = [] results.each do |row| values << headers.map { |h| row[h] } end [headers, values] end |
#process_resultset(results, adapter) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 47 def process_resultset(results, adapter) if adapter == :postgres return process_explain_results_postgres(results) elsif defined?(::ActiveRecord::Result) && results.is_a?(::ActiveRecord::Result) # Note if adapter is mysql, will only have headers, not values return [results.columns, results.rows] elsif results.is_a?(String) return string_explain_plan_results(results) end case adapter when :mysql2 process_explain_results_mysql2(results) when :mysql process_explain_results_mysql(results) when :sqlite process_explain_results_sqlite(results) end end |
#string_explain_plan_results(results) ⇒ Object
Sequel returns explain plans as just one big pre-formatted String In that case, we send a nil headers array, and the single string wrapped in an array for the values. Note that we don’t use this method for Postgres explain plans, since they need to be passed through the explain plan obfuscator first.
93 94 95 |
# File 'lib/new_relic/agent/database/explain_plan_helpers.rb', line 93 def string_explain_plan_results(results) [nil, [results]] end |