aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb')
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb106
1 files changed, 85 insertions, 21 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 95f254ddd2..92908d9599 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -4,9 +4,9 @@ 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
@@ -32,9 +32,14 @@ module ActiveRecord
def initialize(connection, logger, connection_options, config)
super
+ @visitor = BindSubstitution.new self
configure_connection
end
+ def supports_explain?
+ true
+ end
+
# HELPER METHODS ===========================================
def each_hash(result) # :nodoc:
@@ -61,10 +66,6 @@ module ActiveRecord
@connection.escape(string)
end
- def substitute_at(column, index)
- Arel.sql "\0"
- end
-
# CONNECTION MANAGEMENT ====================================
def active?
@@ -76,6 +77,7 @@ module ActiveRecord
disconnect!
connect
end
+ alias :reset! :reconnect!
# Disconnects from the database if already connected.
# Otherwise, this method does nothing.
@@ -86,12 +88,81 @@ module ActiveRecord
end
end
- def reset!
- disconnect!
- connect
+ # DATABASE STATEMENTS ======================================
+
+ def explain(arel, binds = [])
+ sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
+ start = Time.now
+ result = exec_query(sql, 'EXPLAIN', binds)
+ elapsed = Time.now - start
+
+ ExplainPrettyPrinter.new.pp(result, elapsed)
end
- # DATABASE STATEMENTS ======================================
+ 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
#
@@ -146,8 +217,7 @@ module ActiveRecord
# Returns an array of record hashes with the column names as keys and
# column values as values.
def select(sql, name = nil, binds = [])
- binds = binds.dup
- exec_query(sql.gsub("\0") { quote(*binds.shift.reverse) }, name).to_a
+ exec_query(sql, name)
end
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
@@ -156,18 +226,12 @@ module ActiveRecord
end
alias :create :insert_sql
- def exec_insert(sql, name, binds)
- binds = binds.dup
-
- # Pretend to support bind parameters
- execute sql.gsub("\0") { quote(*binds.shift.reverse) }, name
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
+ execute to_sql(sql, binds), name
end
def exec_delete(sql, name, binds)
- binds = binds.dup
-
- # Pretend to support bind parameters
- execute sql.gsub("\0") { quote(*binds.shift.reverse) }, name
+ execute to_sql(sql, binds), name
@connection.affected_rows
end
alias :exec_update :exec_delete