module TingYun::Agent::Database::ExplainPlanHelpers

Constants

EMPTY_STRING
KNOWN_OPERATIONS
MYSQL2_PREFIX
MYSQL_PREFIX
POSTGRES_PREFIX
QUERY_PLAN
SQLITE_EXPLAIN_COLUMNS
SQLITE_PREFIX
SQL_COMMENT_REGEX

Public Instance Methods

handle_exception_in_explain() { || ... } click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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