aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb657
1 files changed, 330 insertions, 327 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 3e77b92141..5900919e88 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -1,14 +1,15 @@
-require 'active_record/connection_adapters/abstract_adapter'
-require 'active_record/connection_adapters/statement_pool'
-require 'active_record/connection_adapters/mysql/column'
-require 'active_record/connection_adapters/mysql/explain_pretty_printer'
-require 'active_record/connection_adapters/mysql/quoting'
-require 'active_record/connection_adapters/mysql/schema_creation'
-require 'active_record/connection_adapters/mysql/schema_definitions'
-require 'active_record/connection_adapters/mysql/schema_dumper'
-require 'active_record/connection_adapters/mysql/type_metadata'
-
-require 'active_support/core_ext/string/strip'
+require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
+require "active_record/connection_adapters/mysql/column"
+require "active_record/connection_adapters/mysql/explain_pretty_printer"
+require "active_record/connection_adapters/mysql/quoting"
+require "active_record/connection_adapters/mysql/schema_creation"
+require "active_record/connection_adapters/mysql/schema_definitions"
+require "active_record/connection_adapters/mysql/schema_dumper"
+require "active_record/connection_adapters/mysql/type_metadata"
+
+require "active_support/core_ext/string/strip"
+require "active_support/core_ext/regexp"
module ActiveRecord
module ConnectionAdapters
@@ -67,16 +68,16 @@ module ActiveRecord
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
- if version < '5.0.0'
+ if version < "5.0.0"
raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.0."
end
end
- CHARSETS_OF_4BYTES_MAXLEN = ['utf8mb4', 'utf16', 'utf16le', 'utf32']
+ CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
def internal_string_options_for_primary_key # :nodoc:
super.tap { |options|
- options[:collation] = collation.sub(/\A[^_]+/, 'utf8') if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
+ options[:collation] = collation.sub(/\A[^_]+/, "utf8") if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
}
end
@@ -85,7 +86,7 @@ module ActiveRecord
end
def mariadb? # :nodoc:
- full_version =~ /mariadb/i
+ /mariadb/i.match?(full_version)
end
# Returns true, since this connection adapter supports migrations.
@@ -135,9 +136,9 @@ module ActiveRecord
def supports_datetime_with_precision?
if mariadb?
- version >= '5.3.0'
+ version >= "5.3.0"
else
- version >= '5.6.4'
+ version >= "5.6.4"
end
end
@@ -158,7 +159,7 @@ module ActiveRecord
end
def index_algorithms
- { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
+ { default: "ALGORITHM = DEFAULT", copy: "ALGORITHM = COPY", inplace: "ALGORITHM = INPLACE" }
end
# HELPER METHODS ===========================================
@@ -207,7 +208,7 @@ module ActiveRecord
def explain(arel, binds = [])
sql = "EXPLAIN #{to_sql(arel, binds)}"
start = Time.now
- result = exec_query(sql, 'EXPLAIN', binds)
+ result = exec_query(sql, "EXPLAIN", binds)
elapsed = Time.now - start
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
@@ -293,17 +294,17 @@ module ActiveRecord
end
def current_database
- select_value 'SELECT DATABASE() as db'
+ select_value "SELECT DATABASE() as db"
end
# Returns the database character set.
def charset
- show_variable 'character_set_database'
+ show_variable "character_set_database"
end
# Returns the database collation strategy.
def collation
- show_variable 'collation_database'
+ show_variable "collation_database"
end
def tables(name = nil) # :nodoc:
@@ -326,7 +327,7 @@ module ActiveRecord
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(@config[:database])}"
- select_values(sql, 'SCHEMA')
+ select_values(sql, "SCHEMA")
end
def truncate(table_name, name = nil)
@@ -352,11 +353,11 @@ module ActiveRecord
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
- select_values(sql, 'SCHEMA').any?
+ select_values(sql, "SCHEMA").any?
end
def views # :nodoc:
- select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", 'SCHEMA')
+ select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA")
end
def view_exists?(view_name) # :nodoc:
@@ -367,17 +368,17 @@ module ActiveRecord
sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
- select_values(sql, 'SCHEMA').any?
+ select_values(sql, "SCHEMA").any?
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
indexes = []
current_index = nil
- execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
+ execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
each_hash(result) do |row|
if current_index != row[:Key_name]
- next if row[:Key_name] == 'PRIMARY' # skip the primary key
+ next if row[:Key_name] == "PRIMARY" # skip the primary key
current_index = row[:Key_name]
mysql_index_type = row[:Index_type].downcase.to_sym
@@ -409,15 +410,18 @@ module ActiveRecord
end
def table_comment(table_name) # :nodoc:
- select_value(<<-SQL.strip_heredoc, 'SCHEMA')
+ schema, name = extract_schema_qualified_name(table_name)
+
+ select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT table_comment
FROM information_schema.tables
- WHERE table_name=#{quote(table_name)}
+ WHERE table_schema = #{quote(schema)}
+ AND table_name = #{quote(name)}
SQL
end
def create_table(table_name, **options) #:nodoc:
- super(table_name, options: 'ENGINE=InnoDB', **options)
+ super(table_name, options: "ENGINE=InnoDB", **options)
end
def bulk_change_table(table_name, operations) #:nodoc:
@@ -476,7 +480,7 @@ module ActiveRecord
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
default = extract_new_default_value(default_or_changes)
column = column_for(table_name, column_name)
- change_column table_name, column_name, column.sql_type, :default => default
+ change_column table_name, column_name, column.sql_type, default: default
end
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
@@ -486,7 +490,7 @@ module ActiveRecord
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
+ change_column table_name, column_name, column.sql_type, null: null
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
@@ -514,7 +518,7 @@ module ActiveRecord
schema, name = extract_schema_qualified_name(table_name)
- fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
+ fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT fk.referenced_table_name AS 'to_table',
fk.referenced_column_name AS 'primary_key',
fk.column_name AS 'column',
@@ -531,59 +535,65 @@ module ActiveRecord
fk_info.map do |row|
options = {
- column: row['column'],
- name: row['name'],
- primary_key: row['primary_key']
+ column: row["column"],
+ name: row["name"],
+ primary_key: row["primary_key"]
}
- options[:on_update] = extract_foreign_key_action(row['on_update'])
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
end
end
- def table_options(table_name)
+ def table_options(table_name) # :nodoc:
+ table_options = {}
+
create_table_info = create_table_info(table_name)
# strip create_definitions and partition_options
- raw_table_options = create_table_info.sub(/\A.*\n\) /m, '').sub(/\n\/\*!.*\*\/\n\z/m, '').strip
+ raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
# strip AUTO_INCREMENT
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
+ table_options[:options] = raw_table_options
+
# strip COMMENT
- raw_table_options.sub!(/ COMMENT='.+'/, '')
+ if raw_table_options.sub!(/ COMMENT='.+'/, "")
+ table_options[:comment] = table_comment(table_name)
+ end
- raw_table_options
+ table_options
end
# Maps logical Rails types to MySQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil, unsigned = nil)
sql = case type.to_s
- when 'integer'
- integer_to_sql(limit)
- when 'text'
- text_to_sql(limit)
- when 'blob'
- binary_to_sql(limit)
- when 'binary'
- if (0..0xfff) === limit
- "varbinary(#{limit})"
- else
- binary_to_sql(limit)
- end
+ when "integer"
+ integer_to_sql(limit)
+ when "text"
+ text_to_sql(limit)
+ when "blob"
+ binary_to_sql(limit)
+ when "binary"
+ if (0..0xfff) === limit
+ "varbinary(#{limit})"
+ else
+ binary_to_sql(limit)
+ end
else
- super(type, limit, precision, scale)
+ super(type, limit, precision, scale)
end
- sql << ' unsigned' if unsigned && type != :primary_key
+ sql << " unsigned" if unsigned && type != :primary_key
sql
end
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
- select_value("SELECT @@#{name}", 'SCHEMA')
+ select_value("SELECT @@#{name}", "SCHEMA")
rescue ActiveRecord::StatementInvalid
nil
end
@@ -593,7 +603,7 @@ module ActiveRecord
schema, name = extract_schema_qualified_name(table_name)
- select_values(<<-SQL.strip_heredoc, 'SCHEMA')
+ select_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name = 'PRIMARY'
@@ -604,7 +614,7 @@ module ActiveRecord
end
def case_sensitive_comparison(table, attribute, column, value)
- if !value.nil? && column.collation && !column.case_sensitive?
+ if column.collation && !column.case_sensitive?
table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
else
super
@@ -625,10 +635,10 @@ module ActiveRecord
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
- [super, *order_columns].join(', ')
+ [super, *order_columns].join(", ")
end
def strict_mode?
@@ -641,340 +651,333 @@ module ActiveRecord
protected
- def initialize_type_map(m) # :nodoc:
- super
-
- register_class_with_limit m, %r(char)i, MysqlString
-
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
- m.register_type %r(^json)i, MysqlJson.new
-
- register_integer_type m, %r(^bigint)i, limit: 8
- register_integer_type m, %r(^int)i, limit: 4
- register_integer_type m, %r(^mediumint)i, limit: 3
- register_integer_type m, %r(^smallint)i, limit: 2
- register_integer_type m, %r(^tinyint)i, limit: 1
+ def initialize_type_map(m) # :nodoc:
+ super
- m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
- m.alias_type %r(year)i, 'integer'
- m.alias_type %r(bit)i, 'binary'
+ register_class_with_limit m, %r(char)i, MysqlString
+
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
+ m.register_type %r(^json)i, MysqlJson.new
+
+ register_integer_type m, %r(^bigint)i, limit: 8
+ register_integer_type m, %r(^int)i, limit: 4
+ register_integer_type m, %r(^mediumint)i, limit: 3
+ register_integer_type m, %r(^smallint)i, limit: 2
+ register_integer_type m, %r(^tinyint)i, limit: 1
+
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
+ m.alias_type %r(year)i, "integer"
+ m.alias_type %r(bit)i, "binary"
+
+ m.register_type(%r(enum)i) do |sql_type|
+ limit = sql_type[/^enum\((.+)\)/i, 1]
+ .split(",").map { |enum| enum.strip.length - 2 }.max
+ MysqlString.new(limit: limit)
+ end
- m.register_type(%r(enum)i) do |sql_type|
- limit = sql_type[/^enum\((.+)\)/i, 1]
- .split(',').map{|enum| enum.strip.length - 2}.max
- MysqlString.new(limit: limit)
+ m.register_type(%r(^set)i) do |sql_type|
+ limit = sql_type[/^set\((.+)\)/i, 1]
+ .split(",").map { |set| set.strip.length - 1 }.sum - 1
+ MysqlString.new(limit: limit)
+ end
end
- m.register_type(%r(^set)i) do |sql_type|
- limit = sql_type[/^set\((.+)\)/i, 1]
- .split(',').map{|set| set.strip.length - 1}.sum - 1
- MysqlString.new(limit: limit)
+ def register_integer_type(mapping, key, options) # :nodoc:
+ mapping.register_type(key) do |sql_type|
+ if /\bunsigned\z/ === sql_type
+ Type::UnsignedInteger.new(options)
+ else
+ Type::Integer.new(options)
+ end
+ end
end
- end
- def register_integer_type(mapping, key, options) # :nodoc:
- mapping.register_type(key) do |sql_type|
- if /\bunsigned\z/ === sql_type
- Type::UnsignedInteger.new(options)
+ def extract_precision(sql_type)
+ if /time/ === sql_type
+ super || 0
else
- Type::Integer.new(options)
+ super
end
end
- end
- def extract_precision(sql_type)
- if /time/ === sql_type
- super || 0
- else
- super
+ def fetch_type_metadata(sql_type, extra = "")
+ MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
end
- end
- def fetch_type_metadata(sql_type, extra = "")
- MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
- end
-
- 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})"}
+ def add_index_length(quoted_columns, **options)
+ if length = options[:length]
+ case length
+ when Hash
+ quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
+ when Integer
+ quoted_columns.each { |name, column| column << "(#{length})" }
+ end
end
+
+ quoted_columns
end
- return option_strings
- end
+ def add_options_for_index_columns(quoted_columns, **options)
+ quoted_columns = add_index_length(quoted_columns, options)
+ super
+ end
- def quoted_columns_for_index(column_names, options = {})
- option_strings = Hash[column_names.map {|name| [name, '']}]
+ # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
+ ER_DUP_ENTRY = 1062
+ ER_NO_REFERENCED_ROW_2 = 1452
+ ER_DATA_TOO_LONG = 1406
+ ER_LOCK_DEADLOCK = 1213
+
+ def translate_exception(exception, message)
+ case error_number(exception)
+ when ER_DUP_ENTRY
+ RecordNotUnique.new(message)
+ when ER_NO_REFERENCED_ROW_2
+ InvalidForeignKey.new(message)
+ when ER_DATA_TOO_LONG
+ ValueTooLong.new(message)
+ when ER_LOCK_DEADLOCK
+ Deadlocked.new(message)
+ else
+ super
+ end
+ end
- # add index length
- option_strings = add_index_length(option_strings, column_names, options)
+ def add_column_sql(table_name, column_name, type, options = {})
+ td = create_table_definition(table_name)
+ cd = td.new_column_definition(column_name, type, options)
+ schema_creation.accept(AddColumnDefinition.new(cd))
+ end
- # add index sort order
- option_strings = add_index_sort_order(option_strings, column_names, options)
+ def change_column_sql(table_name, column_name, type, options = {})
+ column = column_for(table_name, column_name)
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
- end
+ unless options_include_default?(options)
+ options[:default] = column.default
+ end
- # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
- ER_DUP_ENTRY = 1062
- ER_NO_REFERENCED_ROW_2 = 1452
- ER_DATA_TOO_LONG = 1406
- ER_LOCK_DEADLOCK = 1213
+ unless options.has_key?(:null)
+ options[:null] = column.null
+ end
- def translate_exception(exception, message)
- case error_number(exception)
- when ER_DUP_ENTRY
- RecordNotUnique.new(message)
- when ER_NO_REFERENCED_ROW_2
- InvalidForeignKey.new(message)
- when ER_DATA_TOO_LONG
- ValueTooLong.new(message)
- when ER_LOCK_DEADLOCK
- TransactionSerializationError.new(message)
- else
- super
+ td = create_table_definition(table_name)
+ cd = td.new_column_definition(column.name, type, options)
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
end
- end
-
- def add_column_sql(table_name, column_name, type, options = {})
- td = create_table_definition(table_name)
- cd = td.new_column_definition(column_name, type, options)
- schema_creation.accept(AddColumnDefinition.new(cd))
- end
- def change_column_sql(table_name, column_name, type, options = {})
- column = column_for(table_name, column_name)
+ def rename_column_sql(table_name, column_name, new_column_name)
+ column = column_for(table_name, column_name)
+ options = {
+ default: column.default,
+ null: column.null,
+ auto_increment: column.auto_increment?
+ }
- unless options_include_default?(options)
- options[:default] = column.default
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", "SCHEMA")["Type"]
+ td = create_table_definition(table_name)
+ cd = td.new_column_definition(new_column_name, current_type, options)
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
end
- unless options.has_key?(:null)
- options[:null] = column.null
+ def remove_column_sql(table_name, column_name, type = nil, options = {})
+ "DROP #{quote_column_name(column_name)}"
end
- td = create_table_definition(table_name)
- cd = td.new_column_definition(column.name, type, options)
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
- end
-
- def rename_column_sql(table_name, column_name, new_column_name)
- column = column_for(table_name, column_name)
- options = {
- default: column.default,
- null: column.null,
- auto_increment: column.auto_increment?
- }
-
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
- td = create_table_definition(table_name)
- cd = td.new_column_definition(new_column_name, current_type, options)
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
- end
-
- def remove_column_sql(table_name, column_name, type = nil, options = {})
- "DROP #{quote_column_name(column_name)}"
- end
-
- def remove_columns_sql(table_name, *column_names)
- column_names.map {|column_name| remove_column_sql(table_name, column_name) }
- end
+ def remove_columns_sql(table_name, *column_names)
+ column_names.map { |column_name| remove_column_sql(table_name, column_name) }
+ end
- def add_index_sql(table_name, column_name, options = {})
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
- index_algorithm[0, 0] = ", " if index_algorithm.present?
- "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
- end
+ def add_index_sql(table_name, column_name, options = {})
+ index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
+ index_algorithm[0, 0] = ", " if index_algorithm.present?
+ "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
+ end
- def remove_index_sql(table_name, options = {})
- index_name = index_name_for_remove(table_name, options)
- "DROP INDEX #{index_name}"
- end
+ def remove_index_sql(table_name, options = {})
+ index_name = index_name_for_remove(table_name, options)
+ "DROP INDEX #{index_name}"
+ end
- 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
+ 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
- def remove_timestamps_sql(table_name, options = {})
- [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
- end
+ def remove_timestamps_sql(table_name, options = {})
+ [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
+ end
private
# 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!
- def subquery_for(key, select)
- subsubselect = select.clone
- subsubselect.projections = [key]
+ 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?
+ # 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
+ subselect = Arel::SelectManager.new(select.engine)
+ subselect.project Arel.sql(key.name)
+ subselect.from subsubselect.as("__active_record_temp")
+ end
- def supports_rename_index?
- mariadb? ? false : version >= '5.7.6'
- end
+ def supports_rename_index?
+ mariadb? ? false : version >= "5.7.6"
+ end
- def configure_connection
- variables = @config.fetch(:variables, {}).stringify_keys
+ def configure_connection
+ variables = @config.fetch(:variables, {}).stringify_keys
- # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
- variables['sql_auto_is_null'] = 0
+ # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
+ 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?(Integer)
- variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
+ # Increase timeout so the server doesn't disconnect us.
+ wait_timeout = @config[:wait_timeout]
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
+ variables["wait_timeout"] = self.class.type_cast_config_to_integer(wait_timeout)
- defaults = [':default', :default].to_set
+ defaults = [":default", :default].to_set
- # Make MySQL reject illegal values rather than truncating or blanking them, see
- # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
- # If the user has provided another value for sql_mode, don't replace it.
- if sql_mode = variables.delete('sql_mode')
- sql_mode = quote(sql_mode)
- elsif !defaults.include?(strict_mode?)
- if strict_mode?
- sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
- else
- sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
- sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
- sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
+ # Make MySQL reject illegal values rather than truncating or blanking them, see
+ # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
+ # If the user has provided another value for sql_mode, don't replace it.
+ if sql_mode = variables.delete("sql_mode")
+ sql_mode = quote(sql_mode)
+ elsif !defaults.include?(strict_mode?)
+ if strict_mode?
+ sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
+ else
+ sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
+ sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
+ sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
+ end
+ sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
end
- sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
- end
- sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
-
- # NAMES does not have an equals sign, see
- # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
- # (trailing comma because variable_assignments will always have content)
- if @config[:encoding]
- encoding = "NAMES #{@config[:encoding]}"
- encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
- encoding << ", "
- end
-
- # Gather up all of the SET variables...
- variable_assignments = variables.map do |k, v|
- if defaults.include?(v)
- "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
- elsif !v.nil?
- "@@SESSION.#{k} = #{quote(v)}"
+ sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
+
+ # NAMES does not have an equals sign, see
+ # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
+ # (trailing comma because variable_assignments will always have content)
+ if @config[:encoding]
+ encoding = "NAMES #{@config[:encoding]}"
+ encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
+ encoding << ", "
end
- # or else nil; compact to clear nils out
- end.compact.join(', ')
- # ...and send them all in one query
- @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
- end
+ # Gather up all of the SET variables...
+ variable_assignments = variables.map do |k, v|
+ if defaults.include?(v)
+ "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
+ elsif !v.nil?
+ "@@SESSION.#{k} = #{quote(v)}"
+ end
+ # or else nil; compact to clear nils out
+ end.compact.join(", ")
- def column_definitions(table_name) # :nodoc:
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
- each_hash(result)
+ # ...and send them all in one query
+ @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
end
- end
- def extract_foreign_key_action(specifier) # :nodoc:
- case specifier
- when 'CASCADE'; :cascade
- when 'SET NULL'; :nullify
+ def column_definitions(table_name) # :nodoc:
+ execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
+ each_hash(result)
+ end
end
- end
-
- def create_table_info(table_name) # :nodoc:
- select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
- end
- def create_table_definition(*args) # :nodoc:
- MySQL::TableDefinition.new(*args)
- end
+ def extract_foreign_key_action(specifier) # :nodoc:
+ case specifier
+ when "CASCADE"; :cascade
+ when "SET NULL"; :nullify
+ end
+ end
- def extract_schema_qualified_name(string) # :nodoc:
- schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
- schema, name = @config[:database], schema unless name
- [schema, name]
- end
+ def create_table_info(table_name) # :nodoc:
+ select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
+ end
- def integer_to_sql(limit) # :nodoc:
- case limit
- when 1; 'tinyint'
- when 2; 'smallint'
- when 3; 'mediumint'
- when nil, 4; 'int'
- when 5..8; 'bigint'
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
+ def create_table_definition(*args) # :nodoc:
+ MySQL::TableDefinition.new(*args)
end
- end
- def text_to_sql(limit) # :nodoc:
- 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 byte length #{limit}")
+ def extract_schema_qualified_name(string) # :nodoc:
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
+ schema, name = @config[:database], schema unless name
+ [schema, name]
end
- end
- def binary_to_sql(limit) # :nodoc:
- case limit
- when 0..0xff; 'tinyblob'
- when nil, 0x100..0xffff; 'blob'
- when 0x10000..0xffffff; 'mediumblob'
- when 0x1000000..0xffffffff; 'longblob'
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
+ def integer_to_sql(limit) # :nodoc:
+ case limit
+ when 1; "tinyint"
+ when 2; "smallint"
+ when 3; "mediumint"
+ when nil, 4; "int"
+ when 5..8; "bigint"
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
+ end
end
- end
- class MysqlJson < Type::Internal::AbstractJson # :nodoc:
- def changed_in_place?(raw_old_value, new_value)
- # Normalization is required because MySQL JSON data format includes
- # the space between the elements.
- super(serialize(deserialize(raw_old_value)), new_value)
+ def text_to_sql(limit) # :nodoc:
+ 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 byte length #{limit}")
+ end
end
- end
- class MysqlString < Type::String # :nodoc:
- def serialize(value)
- case value
- when true then MySQL::Quoting::QUOTED_TRUE
- when false then MySQL::Quoting::QUOTED_FALSE
- else super
+ def binary_to_sql(limit) # :nodoc:
+ case limit
+ when 0..0xff; "tinyblob"
+ when nil, 0x100..0xffff; "blob"
+ when 0x10000..0xffffff; "mediumblob"
+ when 0x1000000..0xffffffff; "longblob"
+ else raise(ActiveRecordError, "No binary type has byte length #{limit}")
end
end
- private
+ class MysqlJson < Type::Internal::AbstractJson # :nodoc:
+ def changed_in_place?(raw_old_value, new_value)
+ # Normalization is required because MySQL JSON data format includes
+ # the space between the elements.
+ super(serialize(deserialize(raw_old_value)), new_value)
+ end
+ end
- def cast_value(value)
- case value
- when true then MySQL::Quoting::QUOTED_TRUE
- when false then MySQL::Quoting::QUOTED_FALSE
- else super
+ class MysqlString < Type::String # :nodoc:
+ def serialize(value)
+ case value
+ when true then MySQL::Quoting::QUOTED_TRUE
+ when false then MySQL::Quoting::QUOTED_FALSE
+ else super
+ end
end
+
+ private
+
+ def cast_value(value)
+ case value
+ when true then MySQL::Quoting::QUOTED_TRUE
+ when false then MySQL::Quoting::QUOTED_FALSE
+ else super
+ end
+ end
end
- end
- ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
- ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
+ ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
+ ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
end
end
end