diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb')
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb | 88 |
1 files changed, 83 insertions, 5 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 8b574518e5..6086c32dbe 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -1,12 +1,12 @@ require 'active_record/connection_adapters/abstract_mysql_adapter' -gem 'mysql2', '~> 0.3.6' +gem 'mysql2', '~> 0.3.10' require 'mysql2' module ActiveRecord - class Base + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects. - def self.mysql2_connection(config) + def mysql2_connection(config) config[:username] = 'root' if config[:username].nil? if Mysql2::Client.const_defined? :FOUND_ROWS @@ -35,6 +35,10 @@ module ActiveRecord configure_connection end + def supports_explain? + true + end + # HELPER METHODS =========================================== def each_hash(result) # :nodoc: @@ -47,8 +51,8 @@ module ActiveRecord end end - def new_column(field, default, type, null) # :nodoc: - Column.new(field, default, type, null) + def new_column(field, default, type, null, collation) # :nodoc: + Column.new(field, default, type, null, collation) end def error_number(exception) @@ -93,6 +97,80 @@ module ActiveRecord # DATABASE STATEMENTS ====================================== + def explain(arel, binds = []) + sql = "EXPLAIN #{to_sql(arel)}" + start = Time.now + result = exec_query(sql, 'EXPLAIN', binds) + elapsed = Time.now - start + + ExplainPrettyPrinter.new.pp(result, elapsed) + end + + class ExplainPrettyPrinter # :nodoc: + # Pretty prints the result of a EXPLAIN in a way that resembles the output of the + # MySQL shell: + # + # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ + # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | + # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ + # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | | + # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where | + # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ + # 2 rows in set (0.00 sec) + # + # This is an exercise in Ruby hyperrealism :). + def pp(result, elapsed) + widths = compute_column_widths(result) + separator = build_separator(widths) + + pp = [] + + pp << separator + pp << build_cells(result.columns, widths) + pp << separator + + result.rows.each do |row| + pp << build_cells(row, widths) + end + + pp << separator + pp << build_footer(result.rows.length, elapsed) + + pp.join("\n") + "\n" + end + + private + + def compute_column_widths(result) + [].tap do |widths| + result.columns.each_with_index do |column, i| + cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s} + widths << cells_in_column.map(&:length).max + end + end + end + + def build_separator(widths) + padding = 1 + '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+' + end + + def build_cells(items, widths) + cells = [] + items.each_with_index do |item, i| + item = 'NULL' if item.nil? + justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust' + cells << item.to_s.send(justifier, widths[i]) + end + '| ' + cells.join(' | ') + ' |' + end + + def build_footer(nrows, elapsed) + rows_label = nrows == 1 ? 'row' : 'rows' + "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed + end + end + # FIXME: re-enable the following once a "better" query_cache solution is in core # # The overrides below perform much better than the originals in AbstractAdapter |