Module: TingYun::Agent::Database::ExplainPlanHelpers
- Included in:
- Statement
- Defined in:
- lib/ting_yun/agent/database/explain_plan_helpers.rb
Constant Summary collapse
- SQL_COMMENT_REGEX =
Regexp.new('/\*.*?\*/', Regexp::MULTILINE).freeze
- EMPTY_STRING =
''.freeze
- KNOWN_OPERATIONS =
[ 'alter', 'select', 'update', 'delete', 'insert', 'create', 'show', 'set', 'exec', 'execute', 'call' ]
- POSTGRES_PREFIX =
'postgres'.freeze
- MYSQL_PREFIX =
'mysql'.freeze
- MYSQL2_PREFIX =
'mysql2'.freeze
- SQLITE_PREFIX =
'sqlite'.freeze
- 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
- #parameterized?(sql) ⇒ Boolean
- #parse_operation_from_query(sql) ⇒ Object
- #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(adpater, results) ⇒ Object
- #symbolized_adapter(adapter) ⇒ Object
Instance Method Details
#handle_exception_in_explain ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 9 def handle_exception_in_explain yield rescue => e begin # guarantees no throw from explain_sql ::TingYun::Agent.logger.error("Error getting query plan:", e) nil rescue # double exception. throw up your hands nil end end |
#is_select?(sql) ⇒ Boolean
22 23 24 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 22 def is_select?(sql) parse_operation_from_query(sql) == 'select' end |
#parameterized?(sql) ⇒ Boolean
26 27 28 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 26 def parameterized?(sql) TingYun::Agent::Database::Obfuscator.instance.obfuscate_single_quote_literals(sql) =~ /\$\d+/ end |
#parse_operation_from_query(sql) ⇒ Object
48 49 50 51 52 53 54 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 48 def parse_operation_from_query(sql) sql = TingYun::Helper.correctly_encoded(sql).gsub(SQL_COMMENT_REGEX, EMPTY_STRING) if sql =~ /(\w+)/ op = $1.downcase return op if KNOWN_OPERATIONS.include?(op) end end |
#process_explain_results_mysql(results) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 136 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 {"dialect"=> "MySQL", "keys"=>headers, "values"=>values} end |
#process_explain_results_mysql2(results) ⇒ Object
129 130 131 132 133 134 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 129 def process_explain_results_mysql2(results) headers = results.fields values = [] results.each { |row| values << row } {"dialect"=> "MySQL", "keys"=>headers, "values"=>values} end |
#process_explain_results_postgres(results) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 105 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 TingYun::Agent::Database.record_sql_method("nbs.action_tracer.record_sql") == :raw query_plan_string = TingYun::Agent::Database::Obfuscator.instance.obfuscate_postgres_explain(query_plan_string) end values = query_plan_string.split("\n").map { |line| [line] } {"dialect"=> "PostgreSQL", "keys"=>[QUERY_PLAN], "values"=>values} end |
#process_explain_results_sqlite(results) ⇒ Object
159 160 161 162 163 164 165 166 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 159 def process_explain_results_sqlite(results) headers = SQLITE_EXPLAIN_COLUMNS values = [] results.each do |row| values << headers.map { |h| row[h] } end {"dialect"=> "sqlite", "keys"=>headers, "values"=>values} end |
#process_resultset(results, adapter) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 81 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) else return {} end end |
#string_explain_plan_results(adpater, results) ⇒ Object
125 126 127 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 125 def string_explain_plan_results(adpater, results) {"dialect"=> adpater, "keys"=>[], "values"=>[results]} end |
#symbolized_adapter(adapter) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/ting_yun/agent/database/explain_plan_helpers.rb', line 64 def symbolized_adapter(adapter) if adapter.start_with? POSTGRES_PREFIX :postgres elsif adapter == MYSQL_PREFIX :mysql # For the purpose of fetching explain plans, we need to maintain the distinction # between usage of mysql and mysql2. Obfuscation is the same, though. elsif adapter == MYSQL2_PREFIX :mysql2 elsif adapter.start_with? SQLITE_PREFIX :sqlite else adapter.to_sym end end |