W3cubDocs

/Ruby on Rails 4.2

class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter

Parent:
ActiveRecord::ConnectionAdapters::AbstractAdapter

Constants

INDEX_TYPES
INDEX_USINGS
LOST_CONNECTION_ERROR_MESSAGES
NATIVE_DATABASE_TYPES
QUOTED_FALSE

Public Class Methods

emulate_booleans() Show 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 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 139
class_attribute :emulate_booleans
new(connection, logger, connection_options, config) Show source

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

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

  @visitor = Arel::Visitors::MySQL.new self

  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
    @prepared_statements = true
  else
    @prepared_statements = false
  end
end

Public Instance Methods

begin_db_transaction() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 324
def begin_db_transaction
  execute "BEGIN"
end
begin_isolated_db_transaction(isolation) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 328
def begin_isolated_db_transaction(isolation)
  execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
  begin_db_transaction
end
case_insensitive_comparison(table, attribute, column, value) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 651
def case_insensitive_comparison(table, attribute, column, value)
  if column.case_sensitive?
    super
  else
    table[attribute].eq(value)
  end
end
case_sensitive_comparison(table, attribute, column, value) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 643
def case_sensitive_comparison(table, attribute, column, value)
  if column.case_sensitive?
    table[attribute].eq(value)
  else
    super
  end
end
case_sensitive_modifier(node, table_attribute) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 638
def case_sensitive_modifier(node, table_attribute)
  node = Arel::Nodes.build_quoted node, table_attribute
  Arel::Nodes::Bin.new(node)
end
change_column_null(table_name, column_name, null, default = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 520
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() Show source

Returns the database character set.

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 396
def charset
  show_variable 'character_set_database'
end
clear_cache!() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 302
def clear_cache!
  super
  reload_type_map
end
collation() Show source

Returns the database collation strategy.

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 401
def collation
  show_variable 'collation_database'
end
create_database(name, options = {}) Show 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 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 375
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
current_database() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 391
def current_database
  select_value 'SELECT DATABASE() as db'
end
data_source_exists?(name)
Alias for: table_exists?
drop_table(table_name, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 501
def drop_table(table_name, options = {})
  execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
empty_insert_statement_value() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 353
def empty_insert_statement_value
  "VALUES ()"
end
execute(sql, name = nil) Show source

Executes the SQL statement in the context of this connection.

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 308
def execute(sql, name = nil)
  log(sql, name) { @connection.query(sql) }
end
foreign_keys(table_name) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 544
      def foreign_keys(table_name)
        fk_info = select_all "          SELECT fk.referenced_table_name as 'to_table'
                ,fk.referenced_column_name as 'primary_key'
                ,fk.column_name as 'column'
                ,fk.constraint_name as 'name'
          FROM information_schema.key_column_usage fk
          WHERE fk.referenced_column_name is not null
            AND fk.table_schema = '#{@config[:database]}'
            AND fk.table_name = '#{table_name}'
".strip_heredoc

        create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]

        fk_info.map do |row|
          options = {
            column: row['column'],
            name: row['name'],
            primary_key: row['primary_key']
          }

          options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
          options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")

          ForeignKeyDefinition.new(table_name, row['to_table'], options)
        end
      end
index_algorithms() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 229
def index_algorithms
  { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
end
native_database_types() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 225
def native_database_types
  NATIVE_DATABASE_TYPES
end
pk_and_sequence_for(table) Show source

Returns a table's primary key and belonging sequence.

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 620
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) Show source

Returns just a table's primary key

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 633
def primary_key(table)
  pk_and_sequence = pk_and_sequence_for(table)
  pk_and_sequence && pk_and_sequence.first
end
quoted_false() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 277
def quoted_false
  QUOTED_FALSE
end
quoted_true() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 269
def quoted_true
  QUOTED_TRUE
end
recreate_database(name, options = {}) Show source

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

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 361
def recreate_database(name, options = {})
  drop_database(name)
  sql = create_database(name, options)
  reconnect!
  sql
end
rename_index(table_name, old_name, new_name) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 505
def rename_index(table_name, old_name, new_name)
  if supports_rename_index?
    validate_index_length!(table_name, new_name)

    execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
  else
    super
  end
end
rename_table(table_name, new_name) Show source

Renames a table.

Example:

rename_table('octopuses', 'octopi')
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 496
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
schema_creation() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 57
def schema_creation
  SchemaCreation.new self
end
show_variable(name) Show source

SHOW VARIABLES LIKE 'name'

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 612
def show_variable(name)
  variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
  variables.first['Value'] unless variables.empty?
rescue ActiveRecord::StatementInvalid
  nil
end
strict_mode?() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 674
def strict_mode?
  self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
end
supports_datetime_with_precision?() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 221
def supports_datetime_with_precision?
  version >= '5.6.4'
end
supports_foreign_keys?() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 213
def supports_foreign_keys?
  true
end
supports_index_sort_order?() Show 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 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 197
def supports_index_sort_order?
  true
end
supports_indexes_in_create?() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 209
def supports_indexes_in_create?
  true
end
supports_migrations?() Show source

Returns true, since this connection adapter supports migrations.

# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 183
def supports_migrations?
  true
end
supports_primary_key?() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 187
def supports_primary_key?
  true
end
supports_transaction_isolation?() Show 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 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 205
def supports_transaction_isolation?
  version >= '5.0.0'
end
supports_views?() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 217
def supports_views?
  version >= '5.0.0'
end
table_exists?(name) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 420
def table_exists?(name)
  return false unless name.present?
  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
Also aliased as: data_source_exists?
truncate(table_name, name = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 416
def truncate(table_name, name = nil)
  execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
end
type_to_sql(type, limit = nil, precision = nil, scale = nil) Show source

Maps logical Rails types to MySQL-specific data types.

Calls superclass method
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 573
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
  when 'datetime'
    return super unless precision

    case precision
      when 0..6; "datetime(#{precision})"
      else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
    end
  else
    super
  end
end
unquoted_false() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 281
def unquoted_false
  0
end
unquoted_true() Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 273
def unquoted_true
  1
end
valid_type?(type) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 678
def valid_type?(type)
  !native_database_types[type].nil?
end

Protected Instance Methods

add_column_sql(table_name, column_name, type, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 784
def add_column_sql(table_name, column_name, type, options = {})
  td = create_table_definition table_name, options[:temporary], options[:options]
  cd = td.new_column_definition(column_name, type, options)
  schema_creation.visit_AddColumn cd
end
add_index_length(option_strings, column_names, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 748
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 Integer
      column_names.each {|name| option_strings[name] += "(#{length})"}
    end
  end

  return option_strings
end
add_index_sql(table_name, column_name, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 826
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, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 836
def add_timestamps_sql(table_name, options = {})
  [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
end
change_column_sql(table_name, column_name, type, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 790
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

  options[:name] = column.name
  schema_creation.accept ChangeColumnDefinition.new column, type, options
end
quoted_columns_for_index(column_names, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 761
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 = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 818
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) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 822
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 = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 831
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, options = {}) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 840
def remove_timestamps_sql(table_name, options = {})
  [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) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 805
def rename_column_sql(table_name, column_name, new_column_name)
  column  = column_for(table_name, column_name)
  options = {
    name: new_column_name,
    default: column.default,
    null: column.null,
    auto_increment: column.extra == "auto_increment"
  }

  current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
  schema_creation.accept ChangeColumnDefinition.new column, current_type, options
end
subquery_for(key, select) Show 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 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 735
def subquery_for(key, select)
  subsubselect = select.clone
  subsubselect.projections = [key]

  # Materialize subquery by adding distinct
  # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
  subsubselect.distinct unless select.limit || select.offset || select.orders.any?

  subselect = Arel::SelectManager.new(select.engine)
  subselect.project Arel.sql(key.name)
  subselect.from subsubselect.as('__active_record_temp')
end
translate_exception(exception, message) Show source
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 773
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

© 2004–2017 David Heinemeier Hansson
Licensed under the MIT License.