class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter

Constants

INDEX_TYPES
INDEX_USINGS
LOST_CONNECTION_ERROR_MESSAGES
NATIVE_DATABASE_TYPES
QUOTED_FALSE

Public Class Methods

emulate_booleans() click to toggle source

By default, the MysqlAdapter will consider all columns of type tinyint(1) as boolean. If you wish to disable this emulation (which was the default behavior in versions 0.13.1 and earlier) you can add the following line to your application.rb file:

ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 128
class_attribute :emulate_booleans
new(connection, logger, connection_options, config) click to toggle source

FIXME: Make the first parameter more similar for the two adapters

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 162
def initialize(connection, logger, connection_options, config)
  super(connection, logger)
  @connection_options, @config = connection_options, config
  @quoted_column_names, @quoted_table_names = {}, {}

  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
    @visitor = Arel::Visitors::MySQL.new self
  else
    @visitor = unprepared_visitor
  end
end

Public Instance Methods

add_column_position!(sql, options) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 557
def add_column_position!(sql, options)
  if options[:first]
    sql << " FIRST"
  elsif options[:after]
    sql << " AFTER #{quote_column_name(options[:after])}"
  end
end
begin_db_transaction() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 308
def begin_db_transaction
  execute "BEGIN"
rescue
  # Transactions aren't supported
end
begin_isolated_db_transaction(isolation) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 314
def begin_isolated_db_transaction(isolation)
  execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
  begin_db_transaction
rescue
  # Transactions aren't supported
end
case_insensitive_comparison(table, attribute, column, value) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 594
def case_insensitive_comparison(table, attribute, column, value)
  if column.case_sensitive?
    super
  else
    table[attribute].eq(value)
  end
end
case_sensitive_modifier(node) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 590
def case_sensitive_modifier(node)
  Arel::Nodes::Bin.new(node)
end
change_column_default(table_name, column_name, default) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 496
def change_column_default(table_name, column_name, default)
  column = column_for(table_name, column_name)
  change_column table_name, column_name, column.sql_type, :default => default
end
change_column_null(table_name, column_name, null, default = nil) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 501
def change_column_null(table_name, column_name, null, default = nil)
  column = column_for(table_name, column_name)

  unless null || default.nil?
    execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  end

  change_column table_name, column_name, column.sql_type, :null => null
end
charset() click to toggle source

Returns the database character set.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 400
def charset
  show_variable 'character_set_database'
end
collation() click to toggle source

Returns the database collation strategy.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 405
def collation
  show_variable 'collation_database'
end
create_database(name, options = {}) click to toggle source

Create a new MySQL database with optional :charset and :collation. Charset defaults to utf8.

Example:

create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
create_database 'matt_development'
create_database 'matt_development', charset: :big5
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 379
def create_database(name, options = {})
  if options[:collation]
    execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
  else
    execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
  end
end
create_savepoint() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 333
def create_savepoint
  execute("SAVEPOINT #{current_savepoint_name}")
end
current_database() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 395
def current_database
  select_value 'SELECT DATABASE() as db'
end
empty_insert_statement_value() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 357
def empty_insert_statement_value
  "VALUES ()"
end
execute(sql, name = nil) click to toggle source

Executes the SQL statement in the context of this connection.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 282
def execute(sql, name = nil)
  if name == :skip_logging
    @connection.query(sql)
  else
    log(sql, name) { @connection.query(sql) }
  end
rescue ActiveRecord::StatementInvalid => exception
  if exception.message.split(":").first =~ /Packets out of order/
    raise ActiveRecord::StatementInvalid.new("'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings.", exception.original_exception)
  else
    raise
  end
end
index_algorithms() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 214
def index_algorithms
  { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
end
limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 602
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  where_sql
end
native_database_types() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 210
def native_database_types
  NATIVE_DATABASE_TYPES
end
pk_and_sequence_for(table) click to toggle source

Returns a table's primary key and belonging sequence.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 572
def pk_and_sequence_for(table)
  execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
    create_table = each_hash(result).first[:"Create Table"]
    if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
      keys = $1.split(",").map { |key| key.delete('`"') }
      keys.length == 1 ? [keys.first, nil] : nil
    else
      nil
    end
  end
end
primary_key(table) click to toggle source

Returns just a table's primary key

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 585
def primary_key(table)
  pk_and_sequence = pk_and_sequence_for(table)
  pk_and_sequence && pk_and_sequence.first
end
quote(value, column = nil) click to toggle source

QUOTING ==================================================

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 239
def quote(value, column = nil)
  if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
    s = column.class.string_to_binary(value).unpack("H*")[0]
    "x'#{s}'"
  elsif value.kind_of?(BigDecimal)
    value.to_s("F")
  else
    super
  end
end
quoted_false() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 262
def quoted_false
  QUOTED_FALSE
end
quoted_true() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 258
def quoted_true
  QUOTED_TRUE
end
recreate_database(name, options = {}) click to toggle source

Drops the database specified on the name attribute and creates it again using the provided options.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 365
def recreate_database(name, options = {})
  drop_database(name)
  sql = create_database(name, options)
  reconnect!
  sql
end
release_savepoint() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 341
def release_savepoint
  execute("RELEASE SAVEPOINT #{current_savepoint_name}")
end
rename_table(table_name, new_name) click to toggle source

Renames a table.

Example:

rename_table('octopuses', 'octopi')
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 491
def rename_table(table_name, new_name)
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
  rename_table_indexes(table_name, new_name)
end
rollback_to_savepoint() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 337
def rollback_to_savepoint
  execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
end
schema_creation() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 23
def schema_creation
  SchemaCreation.new self
end
show_variable(name) click to toggle source

SHOW VARIABLES LIKE 'name'

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 566
def show_variable(name)
  variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
  variables.first['Value'] unless variables.empty?
end
strict_mode?() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 606
def strict_mode?
  self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
end
supports_index_sort_order?() click to toggle source

Technically MySQL allows to create indexes with the sort order syntax but at the moment (5.5) it doesn't yet implement them

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 198
def supports_index_sort_order?
  true
end
supports_migrations?() click to toggle source

Returns true, since this connection adapter supports migrations.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 179
def supports_migrations?
  true
end
supports_primary_key?() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 183
def supports_primary_key?
  true
end
supports_savepoints?() click to toggle source

Returns true, since this connection adapter supports savepoints.

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 188
def supports_savepoints?
  true
end
supports_transaction_isolation?() click to toggle source

MySQL 4 technically support transaction isolation, but it is affected by a bug where the transaction level gets persisted for the whole session:

bugs.mysql.com/bug.php?id=39170

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 206
def supports_transaction_isolation?
  version[0] >= 5
end
table_exists?(name) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 419
def table_exists?(name)
  return false unless name
  return true if tables(nil, nil, name).any?

  name          = name.to_s
  schema, table = name.split('.', 2)

  unless table # A table was provided without a schema
    table  = schema
    schema = nil
  end

  tables(nil, schema, table).any?
end
type_to_sql(type, limit = nil, precision = nil, scale = nil) click to toggle source

Maps logical Rails types to MySQL-specific data types.

Calls superclass method
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 526
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  case type.to_s
  when 'binary'
    case limit
    when 0..0xfff;           "varbinary(#{limit})"
    when nil;                "blob"
    when 0x1000..0xffffffff; "blob(#{limit})"
    else raise(ActiveRecordError, "No binary type has character length #{limit}")
    end
  when 'integer'
    case limit
    when 1; 'tinyint'
    when 2; 'smallint'
    when 3; 'mediumint'
    when nil, 4, 11; 'int(11)'  # compatibility with MySQL default
    when 5..8; 'bigint'
    else raise(ActiveRecordError, "No integer type has byte size #{limit}")
    end
  when 'text'
    case limit
    when 0..0xff;               'tinytext'
    when nil, 0x100..0xffff;    'text'
    when 0x10000..0xffffff;     'mediumtext'
    when 0x1000000..0xffffffff; 'longtext'
    else raise(ActiveRecordError, "No text type has character length #{limit}")
    end
  else
    super
  end
end
valid_type?(type) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 610
def valid_type?(type)
  !native_database_types[type].nil?
end

Protected Instance Methods

add_column_sql(table_name, column_name, type, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 663
def add_column_sql(table_name, column_name, type, options = {})
  add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  add_column_options!(add_column_sql, options)
  add_column_position!(add_column_sql, options)
  add_column_sql
end
add_index_length(option_strings, column_names, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 627
def add_index_length(option_strings, column_names, options = {})
  if options.is_a?(Hash) && length = options[:length]
    case length
    when Hash
      column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
    when Fixnum
      column_names.each {|name| option_strings[name] += "(#{length})"}
    end
  end

  return option_strings
end
add_index_sql(table_name, column_name, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 712
def add_index_sql(table_name, column_name, options = {})
  index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
  "ADD #{index_type} INDEX #{index_name} (#{index_columns})"
end
add_timestamps_sql(table_name) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 722
def add_timestamps_sql(table_name)
  [add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
end
change_column_sql(table_name, column_name, type, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 670
def change_column_sql(table_name, column_name, type, options = {})
  column = column_for(table_name, column_name)

  unless options_include_default?(options)
    options[:default] = column.default
  end

  unless options.has_key?(:null)
    options[:null] = column.null
  end

  change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  add_column_options!(change_column_sql, options)
  add_column_position!(change_column_sql, options)
  change_column_sql
end
quoted_columns_for_index(column_names, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 640
def quoted_columns_for_index(column_names, options = {})
  option_strings = Hash[column_names.map {|name| [name, '']}]

  # add index length
  option_strings = add_index_length(option_strings, column_names, options)

  # add index sort order
  option_strings = add_index_sort_order(option_strings, column_names, options)

  column_names.map {|name| quote_column_name(name) + option_strings[name]}
end
remove_column_sql(table_name, column_name, type = nil, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 704
def remove_column_sql(table_name, column_name, type = nil, options = {})
  "DROP #{quote_column_name(column_name)}"
end
remove_columns_sql(table_name, *column_names) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 708
def remove_columns_sql(table_name, *column_names)
  column_names.map {|column_name| remove_column_sql(table_name, column_name) }
end
remove_index_sql(table_name, options = {}) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 717
def remove_index_sql(table_name, options = {})
  index_name = index_name_for_remove(table_name, options)
  "DROP INDEX #{index_name}"
end
remove_timestamps_sql(table_name) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 726
def remove_timestamps_sql(table_name)
  [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
end
rename_column_sql(table_name, column_name, new_column_name) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 687
def rename_column_sql(table_name, column_name, new_column_name)
  options = {}

  if column = columns(table_name).find { |c| c.name == column_name.to_s }
    options[:default] = column.default
    options[:null] = column.null
    options[:auto_increment] = (column.extra == "auto_increment")
  else
    raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
  end

  current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
  rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
  add_column_options!(rename_column_sql, options)
  rename_column_sql
end
subquery_for(key, select) click to toggle source

MySQL is too stupid to create a temporary table for use subquery, so we have to give it some prompting in the form of a subsubquery. Ugh!

# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 618
def subquery_for(key, select)
  subsubselect = select.clone
  subsubselect.projections = [key]

  subselect = Arel::SelectManager.new(select.engine)
  subselect.project Arel.sql(key.name)
  subselect.from subsubselect.as('__active_record_temp')
end
translate_exception(exception, message) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 652
def translate_exception(exception, message)
  case error_number(exception)
  when 1062
    RecordNotUnique.new(message, exception)
  when 1452
    InvalidForeignKey.new(message, exception)
  else
    super
  end
end

Private Instance Methods

column_for(table_name, column_name) click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 736
def column_for(table_name, column_name)
  unless column = columns(table_name).find { |c| c.name == column_name.to_s }
    raise "No such column: #{table_name}.#{column_name}"
  end
  column
end
configure_connection() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 743
def configure_connection
  variables = @config[:variables] || {}

  # By default, MySQL 'where id is null' selects the last inserted id.
  # Turn this off. http://dev.rubyonrails.org/ticket/6778
  variables[:sql_auto_is_null] = 0

  # Increase timeout so the server doesn't disconnect us.
  wait_timeout = @config[:wait_timeout]
  wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
  variables[:wait_timeout] = self.class.type_cast_config_to_integer(wait_timeout)

  # Make MySQL reject illegal values rather than truncating or blanking them, see
  # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
  # If the user has provided another value for sql_mode, don't replace it.
  if strict_mode? && !variables.has_key?(:sql_mode)
    variables[:sql_mode] = 'STRICT_ALL_TABLES'
  end

  # NAMES does not have an equals sign, see
  # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
  # (trailing comma because variable_assignments will always have content)
  encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]

  # Gather up all of the SET variables...
  variable_assignments = variables.map do |k, v|
    if v == ':default' || v == :default
      "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
    elsif !v.nil?
      "@@SESSION.#{k.to_s} = #{quote(v)}"
    end
    # or else nil; compact to clear nils out
  end.compact.join(', ')

  # ...and send them all in one query
  execute("SET #{encoding} #{variable_assignments}", :skip_logging)
end
supports_views?() click to toggle source
# File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 732
def supports_views?
  version[0] >= 5
end