class NewRelic::Agent::Database::Statement

Constants

DEFAULT_QUERY_NAME
MYSQL2_PREFIX
MYSQL_PREFIX
NEWLINE
POSTGIS_PREFIX
POSTGRES_PREFIX
SQLITE_PREFIX

Attributes

binds[RW]
config[RW]
database_name[RW]
explainer[RW]
host[RW]
name[RW]
port_path_or_id[RW]
sql[RW]

Public Class Methods

new(sql, config = {}, explainer = nil, binds = nil, name = DEFAULT_QUERY_NAME, host = nil, port_path_or_id = nil, database_name = nil) click to toggle source
# File lib/new_relic/agent/database.rb, line 179
def initialize(sql, config = {}, explainer = nil, binds = nil, name = DEFAULT_QUERY_NAME, host = nil, port_path_or_id = nil, database_name = nil)
  @sql = Database.capture_query(sql)
  @config = config
  @explainer = explainer
  @binds = binds
  @name = name
  @host = host
  @port_path_or_id = port_path_or_id
  @database_name = database_name
  @safe_sql = nil
end

Public Instance Methods

adapter() click to toggle source

This takes a connection config hash from ActiveRecord or Sequel and returns a symbol describing the associated database adapter

# File lib/new_relic/agent/database.rb, line 204
def adapter
  return unless @config

  @adapter ||= if @config[:adapter]
    symbolized_adapter(@config[:adapter].to_s.downcase)
  elsif @config[:uri] && @config[:uri].to_s =~ /^jdbc:([^:]+):/
    # This case is for Sequel with the jdbc-mysql, jdbc-postgres, or jdbc-sqlite3 gems.
    symbolized_adapter($1)
  end
end
append_sql(new_sql) click to toggle source
# File lib/new_relic/agent/database.rb, line 231
def append_sql(new_sql)
  return if new_sql.empty?

  @sql = Database.truncate_query(@sql << NEWLINE << new_sql)
end
explain() click to toggle source
# File lib/new_relic/agent/database.rb, line 215
def explain
  return unless explainable?

  handle_exception_in_explain do
    start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    plan = @explainer.call(self)
    ::NewRelic::Agent.record_metric(
      'Supportability/Database/execute_explain_plan',
      Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
    )
    return process_resultset(plan, adapter) if plan
  end
end
safe_sql() click to toggle source

Returns an sql statement that will be in the form most permissable by the config. The format will be safe for transmission to New Relic.

# File lib/new_relic/agent/database.rb, line 193
def safe_sql
  @safe_sql ||= case Database.record_sql_method
    when :obfuscated
      Database.obfuscate_sql(self)
    when :raw
      sql.to_s
  end
end

Private Instance Methods

explainable?() click to toggle source
# File lib/new_relic/agent/database.rb, line 261
def explainable?
  return false unless @explainer && is_select?(@sql)

  if @sql.end_with?(ELLIPSIS)
    NewRelic::Agent.logger.debug('Unable to collect explain plan for truncated query.')
    return false
  end

  if parameterized?(@sql) && (!@binds || @binds.empty?)
    NewRelic::Agent.logger.debug('Unable to collect explain plan for parameter-less parameterized query.')
    return false
  end

  if !SUPPORTED_ADAPTERS_FOR_EXPLAIN.include?(adapter)
    NewRelic::Agent.logger.debug("Not collecting explain plan because an unknown connection adapter ('#{adapter}') was used.")
    return false
  end

  if multiple_queries?(@sql)
    NewRelic::Agent.logger.debug('Unable to collect explain plan for multiple queries.')
    return false
  end

  true
end
symbolized_adapter(adapter) click to toggle source
# File lib/new_relic/agent/database.rb, line 245
def symbolized_adapter(adapter)
  if adapter.start_with?(POSTGRES_PREFIX) || adapter == POSTGIS_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