1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
# frozen_string_literal: true
require "cases/helper"
require "models/car"
if ActiveRecord::Base.lease_connection.supports_explain?
class ExplainTest < ActiveRecord::TestCase
fixtures :cars
def base
ActiveRecord::Base
end
def lease_connection
base.lease_connection
end
def test_relation_explain
message = Car.where(name: "honda").explain.inspect
assert_match(/^EXPLAIN/, message)
end
def test_collecting_queries_for_explain
queries = ActiveRecord::Base.collecting_queries_for_explain do
Car.where(name: "honda").to_a
end
sql, binds = queries[0]
assert_match "SELECT", sql
if binds.any?
assert_equal 1, binds.length
assert_equal "honda", binds.last.value
else
assert_match "honda", sql
end
end
def test_relation_explain_with_average
expected_query = capture_sql {
Car.average(:id)
}.first
message = Car.all.explain.average(:id)
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_count
expected_query = capture_sql {
Car.count
}.first
message = Car.all.explain.count
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_count_and_argument
expected_query = capture_sql {
Car.count(:id)
}.first
message = Car.all.explain.count(:id)
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_minimum
expected_query = capture_sql {
Car.minimum(:id)
}.first
message = Car.all.explain.minimum(:id)
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_maximum
expected_query = capture_sql {
Car.maximum(:id)
}.first
message = Car.all.explain.maximum(:id)
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_sum
expected_query = capture_sql {
Car.sum(:id)
}.first
message = Car.all.explain.sum(:id)
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_first
expected_query = capture_sql {
Car.all.first
}.first
message = Car.all.explain.first
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_last
expected_query = capture_sql {
Car.all.last
}.first
message = Car.all.explain.last
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_pluck
expected_query = capture_sql {
Car.all.pluck
}.first
message = Car.all.explain.pluck
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_relation_explain_with_pluck_with_args
expected_query = capture_sql {
Car.all.pluck(:id, :name)
}.first
message = Car.all.explain.pluck(:id, :name)
assert_match(/^EXPLAIN/, message)
assert_match(expected_query, message)
end
def test_exec_explain_with_no_binds
sqls = %w(foo bar)
binds = [[], []]
queries = sqls.zip(binds)
stub_explain_for_query_plans do
expected = sqls.map { |sql| "#{expected_explain_clause} #{sql}\nquery plan #{sql}" }.join("\n")
assert_equal expected, base.exec_explain(queries)
end
end
def test_exec_explain_with_binds
sqls = %w(foo bar)
binds = [[bind_param("wadus", 1)], [bind_param("chaflan", 2)]]
queries = sqls.zip(binds)
stub_explain_for_query_plans(["query plan foo\n", "query plan bar\n"]) do
expected = <<~SQL
#{expected_explain_clause} #{sqls[0]} [["wadus", 1]]
query plan foo
#{expected_explain_clause} #{sqls[1]} [["chaflan", 2]]
query plan bar
SQL
assert_equal expected, base.exec_explain(queries)
end
end
private
def stub_explain_for_query_plans(query_plans = ["query plan foo", "query plan bar"])
explain_called = 0
# Minitest's `stub` method is unable to correctly replicate method arguments
# signature, so we need to do a manual stubbing in this case.
metaclass = lease_connection.singleton_class
explain_method = metaclass.instance_method(:explain)
metaclass.define_method(:explain) do |_arel, _binds = [], _options = {}|
explain_called += 1
query_plans[explain_called - 1]
end
yield
ensure
if metaclass
metaclass.undef_method(:explain)
metaclass.define_method(:explain, explain_method)
end
end
def bind_param(name, value)
ActiveRecord::Relation::QueryAttribute.new(name, value, ActiveRecord::Type::Value.new)
end
def expected_explain_clause
if lease_connection.respond_to?(:build_explain_clause)
lease_connection.build_explain_clause
else
"EXPLAIN for:"
end
end
end
end
|