module PgHero::Methods::Explain

Public Instance Methods

explain(sql) click to toggle source

TODO remove in 4.0 note: this method is not affected by the explain option

# File lib/pghero/methods/explain.rb, line 6
def explain(sql)
  sql = squish(sql)
  explanation = nil

  # use transaction for safety
  with_transaction(statement_timeout: (explain_timeout_sec * 1000).round, rollback: true) do
    if (sql.delete_suffix(";").include?(";") || sql.upcase.include?("COMMIT")) && !explain_safe?
      raise ActiveRecord::StatementInvalid, "Unsafe statement"
    end
    explanation = execute("EXPLAIN #{sql}").map { |v| v["QUERY PLAN"] }.join("\n")
  end

  explanation
end
explain_v2(sql, analyze: nil, verbose: nil, costs: nil, settings: nil, generic_plan: nil, buffers: nil, wal: nil, timing: nil, summary: nil, format: "text") click to toggle source

TODO rename to explain in 4.0 note: this method is not affected by the explain option

# File lib/pghero/methods/explain.rb, line 23
def explain_v2(sql, analyze: nil, verbose: nil, costs: nil, settings: nil, generic_plan: nil, buffers: nil, wal: nil, timing: nil, summary: nil, format: "text")
  options = []
  add_explain_option(options, "ANALYZE", analyze)
  add_explain_option(options, "VERBOSE", verbose)
  add_explain_option(options, "SETTINGS", settings)
  add_explain_option(options, "GENERIC_PLAN", generic_plan)
  add_explain_option(options, "COSTS", costs)
  add_explain_option(options, "BUFFERS", buffers)
  add_explain_option(options, "WAL", wal)
  add_explain_option(options, "TIMING", timing)
  add_explain_option(options, "SUMMARY", summary)
  options << "FORMAT #{explain_format(format)}"

  explain("(#{options.join(", ")}) #{sql}")
end

Private Instance Methods

add_explain_option(options, name, value) click to toggle source
# File lib/pghero/methods/explain.rb, line 48
def add_explain_option(options, name, value)
  unless value.nil?
    options << "#{name}#{value ? "" : " FALSE"}"
  end
end
explain_format(format) click to toggle source

important! validate format to prevent injection

# File lib/pghero/methods/explain.rb, line 55
def explain_format(format)
  if ["text", "xml", "json", "yaml"].include?(format)
    format.upcase
  else
    raise ArgumentError, "Unknown format"
  end
end
explain_safe?() click to toggle source
# File lib/pghero/methods/explain.rb, line 41
def explain_safe?
  select_all("SELECT 1; SELECT 1")
  false
rescue ActiveRecord::StatementInvalid
  true
end