diff options
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.rb | 112 |
1 files changed, 42 insertions, 70 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 cc057146c7..42d3c68970 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -1,6 +1,7 @@ require 'active_record/connection_adapters/abstract_adapter' 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' @@ -11,6 +12,7 @@ require 'active_support/core_ext/string/strip' module ActiveRecord module ConnectionAdapters class AbstractMysqlAdapter < AbstractAdapter + include MySQL::Quoting include MySQL::ColumnDumper include Savepoints @@ -32,8 +34,6 @@ module ActiveRecord class_attribute :emulate_booleans self.emulate_booleans = true - QUOTED_TRUE, QUOTED_FALSE = '1', '0' - NATIVE_DATABASE_TYPES = { primary_key: "int auto_increment PRIMARY KEY", string: { name: "varchar", limit: 255 }, @@ -54,7 +54,6 @@ module ActiveRecord def initialize(connection, logger, connection_options, config) super(connection, logger, config) - @quoted_column_names, @quoted_table_names = {}, {} @visitor = Arel::Visitors::MySQL.new self @@ -153,8 +152,8 @@ module ActiveRecord raise NotImplementedError end - def new_column(field, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil) # :nodoc: - MySQL::Column.new(field, default, sql_type_metadata, null, default_function, collation) + def new_column(field, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil) # :nodoc: + MySQL::Column.new(field, default, sql_type_metadata, null, table_name, default_function, collation) end # Must return the MySQL error number from the exception, if the exception has an @@ -163,48 +162,6 @@ module ActiveRecord raise NotImplementedError end - # QUOTING ================================================== - - def _quote(value) # :nodoc: - if value.is_a?(Type::Binary::Data) - "x'#{value.hex}'" - else - super - end - end - - def quote_column_name(name) #:nodoc: - @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`" - end - - def quote_table_name(name) #:nodoc: - @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`') - end - - def quoted_true - QUOTED_TRUE - end - - def unquoted_true - 1 - end - - def quoted_false - QUOTED_FALSE - end - - def unquoted_false - 0 - end - - def quoted_date(value) - if supports_datetime_with_precision? - super - else - super.sub(/\.\d{6}\z/, '') - end - end - # REFERENTIAL INTEGRITY ==================================== def disable_referential_integrity #:nodoc: @@ -301,9 +258,9 @@ module ActiveRecord # create_database 'matt_development', charset: :big5 def create_database(name, options = {}) if options[:collation] - execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`" + execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')} COLLATE #{quote_table_name(options[:collation])}" else - execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`" + execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}" end end @@ -312,7 +269,7 @@ module ActiveRecord # Example: # drop_database('sebastian_development') def drop_database(name) #:nodoc: - execute "DROP DATABASE IF EXISTS `#{name}`" + execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}" end def current_database @@ -421,16 +378,15 @@ module ActiveRecord # Returns an array of +Column+ objects for the table specified by +table_name+. def columns(table_name) # :nodoc: - sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}" - execute_and_free(sql, 'SCHEMA') do |result| - each_hash(result).map do |field| - type_metadata = fetch_type_metadata(field[:Type], field[:Extra]) - if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP" - new_column(field[:Field], nil, type_metadata, field[:Null] == "YES", field[:Default], field[:Collation]) - else - new_column(field[:Field], field[:Default], type_metadata, field[:Null] == "YES", nil, field[:Collation]) - end + table_name = table_name.to_s + column_definitions(table_name).map do |field| + type_metadata = fetch_type_metadata(field[:Type], field[:Extra]) + if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP" + default, default_function = nil, field[:Default] + else + default, default_function = field[:Default], nil end + new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation]) end end @@ -607,10 +563,10 @@ module ActiveRecord end def case_sensitive_comparison(table, attribute, column, value) - if value.nil? || column.case_sensitive? - super - else + if !value.nil? && column.collation && !column.case_sensitive? table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new)) + else + super end end @@ -667,7 +623,7 @@ module ActiveRecord register_integer_type m, %r(^smallint)i, limit: 2 register_integer_type m, %r(^tinyint)i, limit: 1 - m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans + 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' @@ -846,9 +802,19 @@ module ActiveRecord # 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. - unless variables.has_key?('sql_mode') || defaults.include?(@config[:strict]) - variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : '' + 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_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 @@ -870,7 +836,13 @@ module ActiveRecord end.compact.join(', ') # ...and send them all in one query - @connection.query "SET #{encoding} #{variable_assignments}" + @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}" + end + + 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 def extract_foreign_key_action(structure, name, action) # :nodoc: @@ -936,8 +908,8 @@ module ActiveRecord class MysqlString < Type::String # :nodoc: def serialize(value) case value - when true then "1" - when false then "0" + when true then MySQL::Quoting::QUOTED_TRUE + when false then MySQL::Quoting::QUOTED_FALSE else super end end @@ -946,8 +918,8 @@ module ActiveRecord def cast_value(value) case value - when true then "1" - when false then "0" + when true then MySQL::Quoting::QUOTED_TRUE + when false then MySQL::Quoting::QUOTED_FALSE else super end end |