module ODBCAdapter module Adapters # Overrides specific to MySQL. Mostly taken from # ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter class MySQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter PRIMARY_KEY = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'.freeze class BindSubstitution < Arel::Visitors::MySQL include Arel::Visitors::BindVisitor end def arel_visitor BindSubstitution.new(self) end # Explicitly turning off prepared statements in the MySQL adapter because # of a weird bug with SQLDescribeParam returning a string type for LIMIT # parameters. This is blocking them from running with an error: # # You have an error in your SQL syntax; ... # ... right syntax to use near ''1'' at line 1: ... def prepared_statements false end def truncate(table_name, name = nil) execute("TRUNCATE TABLE #{quote_table_name(table_name)}", name) end # Quotes a string, escaping any ' (single quote) and \ (backslash) # characters. def quote_string(string) string.gsub(/\\/, '\&\&').gsub(/'/, "''") end def quoted_true '1' end def unquoted_true 1 end def quoted_false '0' end def unquoted_false 0 end def disable_referential_integrity(&block) old = select_value("SELECT @@FOREIGN_KEY_CHECKS") begin update("SET FOREIGN_KEY_CHECKS = 0") yield ensure update("SET FOREIGN_KEY_CHECKS = #{old}") end 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 'rails_development' # create_database 'rails_development', charset: :big5 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 # Drops a MySQL database. # # Example: # drop_database('rails_development') def drop_database(name) execute("DROP DATABASE IF EXISTS `#{name}`") end def create_table(name, options = {}) super(name, { options: 'ENGINE=InnoDB' }.merge(options)) end # Renames a table. def rename_table(name, new_name) execute("RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}") end def change_column(table_name, column_name, type, options = {}) unless options_include_default?(options) options[:default] = column_for(table_name, column_name).default end change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" add_column_options!(change_column_sql, options) execute(change_column_sql) end def change_column_default(table_name, column_name, default_or_changes) 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) end 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 def rename_column(table_name, column_name, new_column_name) column = column_for(table_name, column_name) current_type = column.native_type current_type << "(#{column.limit})" if column.limit execute("ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}") end # Skip primary key indexes def indexes(table_name, name = nil) super(table_name, name).reject { |i| i.unique && i.name =~ /^PRIMARY$/ } end # MySQL 5.x doesn't allow DEFAULT NULL for first timestamp column in a # table def options_include_default?(options) if options.include?(:default) && options[:default].nil? if options.include?(:column) && options[:column].native_type =~ /timestamp/i options.delete(:default) end end super(options) end protected def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) super id_value || last_inserted_id(nil) end def last_inserted_id(_result) select_value('SELECT LAST_INSERT_ID()').to_i end end end end