# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 588
def primary_key(table)
pk_and_sequence = pk_and_sequence_for(table)
pk_and_sequence && pk_and_sequence.first
end
On this page
class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter
Constants
- INDEX_TYPES
- INDEX_USINGS
- LOST_CONNECTION_ERROR_MESSAGES
- NATIVE_DATABASE_TYPES
- QUOTED_FALSE
Public Class Methods
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 141
class_attribute :emulate_booleans
FIXME: Make the first parameter more similar for the two adapters
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 175
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 })
@prepared_statements = true
@visitor = Arel::Visitors::MySQL.new self
else
@visitor = unprepared_visitor
end
end
Public Instance Methods
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 560
def add_column_position!(sql, options)
if options[:first]
sql << " FIRST"
elsif options[:after]
sql << " AFTER #{quote_column_name(options[:after])}"
end
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 318
def begin_db_transaction
execute "BEGIN"
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 322
def begin_isolated_db_transaction(isolation)
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
begin_db_transaction
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 597
def case_insensitive_comparison(table, attribute, column, value)
if column.case_sensitive?
super
else
table[attribute].eq(value)
end
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 593
def case_sensitive_modifier(node)
Arel::Nodes::Bin.new(node)
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 499
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 504
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
Returns the database character set.
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 390
def charset
show_variable 'character_set_database'
end
Returns the database collation strategy.
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 395
def collation
show_variable 'collation_database'
end
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 369
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 385
def current_database
select_value 'SELECT DATABASE() as db'
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 487
def drop_table(table_name, options = {})
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 347
def empty_insert_statement_value
"VALUES ()"
end
Executes the SQL statement in the context of this connection.
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 302
def execute(sql, name = nil)
log(sql, name) { @connection.query(sql) }
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 234
def index_algorithms
{ default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 605
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
where_sql
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 230
def native_database_types
NATIVE_DATABASE_TYPES
end
Returns a table's primary key and belonging sequence.
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 575
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
Returns just a table's primary key
QUOTING ==================================================
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 259
def quote(value, column = nil)
if value.kind_of?(String) && column && column.type == :binary
s = value.unpack("H*")[0]
"x'#{s}'"
elsif value.kind_of?(BigDecimal)
value.to_s("F")
else
super
end
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 282
def quoted_false
QUOTED_FALSE
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 278
def quoted_true
QUOTED_TRUE
end
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 355
def recreate_database(name, options = {})
drop_database(name)
sql = create_database(name, options)
reconnect!
sql
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 491
def rename_index(table_name, old_name, new_name)
if supports_rename_index?
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
Renames a table.
Example:
rename_table('octopuses', 'octopi')
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 482
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 34
def schema_creation
SchemaCreation.new self
end
SHOW VARIABLES LIKE 'name'
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 569
def show_variable(name)
variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
variables.first['Value'] unless variables.empty?
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 609
def strict_mode?
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
end
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 207
def supports_index_sort_order?
true
end
Returns true, since this connection adapter supports migrations.
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 193
def supports_migrations?
true
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 197
def supports_primary_key?
true
end
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 226
def supports_transaction_isolation?
version[0] >= 5
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 409
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 211
def type_cast(value, column)
case value
when TrueClass
1
when FalseClass
0
else
super
end
end
Maps logical Rails types to MySQL-specific data types.
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 529
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 613
def valid_type?(type)
!native_database_types[type].nil?
end
Protected Instance Methods
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 666
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 630
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 710
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 720
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 672
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 643
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 702
def remove_column_sql(table_name, column_name, type = nil, options = {})
"DROP #{quote_column_name(column_name)}"
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 706
def remove_columns_sql(table_name, *column_names)
column_names.map {|column_name| remove_column_sql(table_name, column_name) }
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 715
def remove_index_sql(table_name, options = {})
index_name = index_name_for_remove(table_name, options)
"DROP INDEX #{index_name}"
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 724
def remove_timestamps_sql(table_name)
[remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 687
def rename_column_sql(table_name, column_name, new_column_name)
options = { name: new_column_name }
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"]
schema_creation.accept ChangeColumnDefinition.new column, current_type, options
end
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 621
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 655
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–2016 David Heinemeier Hansson
Licensed under the MIT License.