# 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
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
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 # File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 324 def begin_db_transaction execute "BEGIN" end
# 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 # 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 # 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 # 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
# 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 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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 302 def clear_cache! super reload_type_map end
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 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 # File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 391 def current_database select_value 'SELECT DATABASE() as db' end
# 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 # File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 353 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 308
def execute(sql, name = nil)
log(sql, name) { @connection.query(sql) }
end # 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 # 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 # File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 225 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 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 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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 277 def quoted_false QUOTED_FALSE end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 269 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 361
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 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 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 # File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 57 def schema_creation SchemaCreation.new self end
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 # 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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 221 def supports_datetime_with_precision? version >= '5.6.4' end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 213 def supports_foreign_keys? 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 197 def supports_index_sort_order? true end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 209 def supports_indexes_in_create? true end
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
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 187 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 205 def supports_transaction_isolation? version >= '5.0.0' end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 217 def supports_views? version >= '5.0.0' end
# 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 # 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 Maps logical Rails types to MySQL-specific data types.
# 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 # File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 281 def unquoted_false 0 end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 273 def unquoted_true 1 end
# File activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 678 def valid_type?(type) !native_database_types[type].nil? end
# 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 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 # 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.