Files

Class/Module Index [+]

Quicksearch

ArJdbc::MySQL

Constants

ADAPTER_NAME
ColumnExtensions

@private backwards-compatibility

INDEX_TYPES
INDEX_USINGS
IndexDefinition

@private

NATIVE_DATABASE_TYPES

Public Class Methods

arel_visitor_type(config = nil) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 127
def self.arel_visitor_type(config = nil)
  ::Arel::Visitors::MySQL
end
column_selector() click to toggle source

@see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types

# File lib/arjdbc/mysql/column.rb, line 5
def self.column_selector
  [ /mysql/, lambda { |config, column| column.extend(Column) } ]
end
emulate_booleans() click to toggle source

@deprecated Use {emulate_booleans?} instead.

# File lib/arjdbc/mysql/adapter.rb, line 87
def self.emulate_booleans; @@emulate_booleans; end
emulate_booleans=(emulate) click to toggle source

@see emulate_booleans?

# File lib/arjdbc/mysql/adapter.rb, line 89
def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
emulate_booleans?() click to toggle source

Boolean emulation can be disabled using (or using the adapter method) :

ArJdbc::MySQL.emulate_booleans = false

@see ActiveRecord::ConnectionAdapters::MysqlAdapter#emulate_booleans

# File lib/arjdbc/mysql/adapter.rb, line 85
def self.emulate_booleans?; @@emulate_booleans; end
jdbc_connection_class() click to toggle source

@see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class

# File lib/arjdbc/mysql/adapter.rb, line 15
def self.jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
end

Public Instance Methods

adapter_name() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 123
def adapter_name
  ADAPTER_NAME
end
add_column(table_name, column_name, type, options = {}) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 393
def add_column(table_name, column_name, type, options = {})
  add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  add_column_options!(add_column_sql, options)
  add_column_position!(add_column_sql, options)
  execute(add_column_sql)
end
add_column_position!(sql, options) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 454
def add_column_position!(sql, options)
  if options[:first]
    sql << " FIRST"
  elsif options[:after]
    sql << " AFTER #{quote_column_name(options[:after])}"
  end
end
add_limit_offset!(sql, options) click to toggle source

@note Only used with (non-AREL) ActiveRecord *2.3*. @see Arel::Visitors::MySQL

# File lib/arjdbc/mysql/adapter.rb, line 464
def add_limit_offset!(sql, options)
  limit, offset = options[:limit], options[:offset]
  if limit && offset
    sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
  elsif limit
    sql << " LIMIT #{sanitize_limit(limit)}"
  elsif offset
    sql << " OFFSET #{offset.to_i}"
  end
  sql
end
case_sensitive_equality_operator() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 137
def case_sensitive_equality_operator
  "= BINARY"
end
case_sensitive_modifier(node) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 141
def case_sensitive_modifier(node)
  Arel::Nodes::Bin.new(node)
end
change_column(table_name, column_name, type, options = {}) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 416
def change_column(table_name, column_name, type, options = {})
  column = column_for(table_name, column_name)

  unless options_include_default?(options)
    # NOTE: no defaults for BLOB/TEXT columns with MySQL
    options[:default] = column.default if type != :text && type != :binary
  end

  unless options.has_key?(:null)
    options[:null] = column.null
  end

  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  add_column_options!(change_column_sql, options)
  add_column_position!(change_column_sql, options)
  execute(change_column_sql)
end
change_column_default(table_name, column_name, default) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 400
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
change_column_null(table_name, column_name, null, default = nil) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 405
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
charset() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 504
def charset
  show_variable("character_set_database")
end
collation() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 508
def collation
  show_variable("collation_database")
end
columns(table_name, name = nil) click to toggle source

Returns an array of `Column` objects for the table specified. @override

# File lib/arjdbc/mysql/adapter.rb, line 331
def columns(table_name, name = nil)
  sql = "SHOW FULL COLUMNS FROM #{quote_table_name(table_name)}"
  columns = execute(sql, name || 'SCHEMA')
  strict = strict_mode?
  column = ::ActiveRecord::ConnectionAdapters::MysqlAdapter::Column
  pass_cast_type = respond_to?(:lookup_cast_type)
  columns.map! do |field|
    sql_type = field['Type']
    null = field['Null'] == "YES"
    if pass_cast_type
      cast_type = lookup_cast_type(sql_type)
      column.new(field['Field'], field['Default'], cast_type, sql_type, null, field['Collation'], strict, field['Extra'])
    else
      column.new(field['Field'], field['Default'], sql_type, null, field['Collation'], strict, field['Extra'])
    end
  end
  columns
end
configure_connection() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 35
def configure_connection
  variables = config[:variables] || {}
  # By default, MySQL 'where id is null' selects the last inserted id. Turn this off.
  variables[:sql_auto_is_null] = 0 # execute "SET SQL_AUTO_IS_NULL=0"

  # Increase timeout so the server doesn't disconnect us.
  wait_timeout = config[:wait_timeout]
  wait_timeout = self.class.type_cast_config_to_integer(wait_timeout)
  variables[:wait_timeout] = wait_timeout.is_a?(Fixnum) ? wait_timeout : 2147483

  # Make MySQL reject illegal values rather than truncating or blanking them, see
  # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
  # If the user has provided another value for sql_mode, don't replace it.
  if strict_mode? && ! variables.has_key?(:sql_mode)
    variables[:sql_mode] = 'STRICT_ALL_TABLES' # SET SQL_MODE='STRICT_ALL_TABLES'
  end

  # NAMES does not have an equals sign, see
  # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
  # (trailing comma because variable_assignments will always have content)
  encoding = "NAMES #{config[:encoding]}, " if config[:encoding]

  # Gather up all of the SET variables...
  variable_assignments = variables.map do |k, v|
    if v == ':default' || v == :default
      "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
    elsif ! v.nil?
      "@@SESSION.#{k.to_s} = #{quote(v)}"
    end
    # or else nil; compact to clear nils out
  end.compact.join(', ')

  # ...and send them all in one query
  execute("SET #{encoding} #{variable_assignments}", :skip_logging)
end
create_database(name, options = {}) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 358
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
create_savepoint(name = current_savepoint_name(true)) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 219
def create_savepoint(name = current_savepoint_name(true))
  log("SAVEPOINT #{name}", 'Savepoint') { super }
end
create_table(name, options = {}) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 376
def create_table(name, options = {})
  super(name, {:options => "ENGINE=InnoDB DEFAULT CHARSET=utf8"}.merge(options))
end
current_database() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 371
def current_database
  select_one("SELECT DATABASE() as db")["db"]
end
disable_referential_integrity() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 233
def disable_referential_integrity
  fk_checks = select_value("SELECT @@FOREIGN_KEY_CHECKS")
  begin
    update("SET FOREIGN_KEY_CHECKS = 0")
    yield
  ensure
    update("SET FOREIGN_KEY_CHECKS = #{fk_checks}")
  end
end
drop_database(name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 367
def drop_database(name)
  execute "DROP DATABASE IF EXISTS `#{name}`"
end
empty_insert_statement_value() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 544
def empty_insert_statement_value
  "VALUES ()"
end
indexes(table_name, name = nil) click to toggle source

Returns an array of indexes for the given table. @override

# File lib/arjdbc/mysql/adapter.rb, line 303
def indexes(table_name, name = nil)
  indexes = []
  current_index = nil
  result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name || 'SCHEMA')
  result.each do |row|
    key_name = row['Key_name']
    if current_index != key_name
      next if key_name == 'PRIMARY' # skip the primary key
      current_index = key_name
      indexes <<
        if self.class.const_defined?(:INDEX_TYPES) # AR 4.0
          mysql_index_type = row['Index_type'].downcase.to_sym
          index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
          index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
          IndexDefinition.new(row['Table'], key_name, row['Non_unique'].to_i == 0, [], [], nil, nil, index_type, index_using)
        else
          IndexDefinition.new(row['Table'], key_name, row['Non_unique'].to_i == 0, [], [])
        end
    end

    indexes.last.columns << row["Column_name"]
    indexes.last.lengths << row["Sub_part"]
  end
  indexes
end
init_connection(jdbc_connection) click to toggle source

@private

# File lib/arjdbc/mysql/adapter.rb, line 20
def init_connection(jdbc_connection)
  meta = jdbc_connection.meta_data
  if meta.driver_major_version == 1 # TODO check in driver code
    # assumes MariaDB 1.x currently
  elsif meta.driver_major_version < 5
    raise ::ActiveRecord::ConnectionNotEstablished,
      "MySQL adapter requires driver >= 5.0 got: '#{meta.driver_version}'"
  elsif meta.driver_major_version == 5 && meta.driver_minor_version < 1
    config[:connection_alive_sql] ||= 'SELECT 1' # need 5.1 for JDBC 4.0
  else
    # NOTE: since the loaded Java driver class can't change :
    MySQL.send(:remove_method, :init_connection) rescue nil
  end
end
join_to_update(update, select) click to toggle source

In the simple case, MySQL allows us to place JOINs directly into the UPDATE query. However, this does not allow for LIMIT, OFFSET and ORDER. To support these, we must use a subquery. However, MySQL is too stupid to create a temporary table for this automatically, so we have to give it some prompting in the form of a subsubquery. Ugh! @private based on mysql_adapter.rb from 3.1-stable

# File lib/arjdbc/mysql/adapter.rb, line 482
def join_to_update(update, select)
  if select.limit || select.offset || select.orders.any?
    subsubselect = select.clone
    subsubselect.projections = [update.key]

    subselect = Arel::SelectManager.new(select.engine)
    subselect.project Arel.sql(update.key.name)
    subselect.from subsubselect.as('__active_record_temp')

    update.where update.key.in(subselect)
  else
    update.table select.source
    update.wheres = select.constraints
  end
end
limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 145
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  where_sql
end
native_database_types() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 116
def native_database_types
  NATIVE_DATABASE_TYPES
end
pk_and_sequence_for(table) click to toggle source

Returns a table’s primary key and belonging sequence. @note Not used, only here for potential compatibility with native adapter. @override

# File lib/arjdbc/mysql/adapter.rb, line 281
def pk_and_sequence_for(table)
  result = execute("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA').first
  if result['Create Table'].to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
    keys = $1.split(","); keys.map! { |key| key.gsub(/[`"]/, "") }
    return keys.length == 1 ? [ keys.first, nil ] : nil
  else
    return nil
  end
end
primary_key(table) click to toggle source

Returns just a table’s primary key. @override

# File lib/arjdbc/mysql/adapter.rb, line 272
def primary_key(table)
  #pk_and_sequence = pk_and_sequence_for(table)
  #pk_and_sequence && pk_and_sequence.first
  @connection.primary_keys(table).first
end
quote(value, column = nil) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 152
def quote(value, column = nil)
  return value.quoted_id if value.respond_to?(:quoted_id)
  return value if sql_literal?(value)
  return value.to_s if column && column.type == :primary_key

  if value.kind_of?(String) && column && column.type == :binary
    "x'#{value.unpack("H*")[0]}'"
  elsif value.kind_of?(BigDecimal)
    value.to_s("F")
  else
    super
  end
end
quote_column_name(name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 167
def quote_column_name(name)
  "`#{name.to_s.gsub('`', '``')}`"
end
quote_table_name(name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 172
def quote_table_name(name)
  quote_column_name(name).gsub('.', '`.`')
end
recreate_database(name, options = {}) click to toggle source

@private

# File lib/arjdbc/mysql/adapter.rb, line 351
def recreate_database(name, options = {})
  drop_database(name)
  create_database(name, options)
  reconnect!
end
release_savepoint(name = current_savepoint_name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 229
def release_savepoint(name = current_savepoint_name)
  log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super }
end
remove_index!(table_name, index_name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 387
def remove_index!(table_name, index_name)
  # missing table_name quoting in AR-2.3
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
end
rename_column(table_name, column_name, new_column_name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 435
def rename_column(table_name, column_name, new_column_name)
  options = {}

  if column = columns(table_name).find { |c| c.name == column_name.to_s }
    type = column.type
    options[:default] = column.default if type != :text && type != :binary
    options[:null] = column.null
  else
    raise ActiveRecord::ActiveRecordError, "No such column: #{table_name}.#{column_name}"
  end

  current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]

  rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
  add_column_options!(rename_column_sql, options)
  execute(rename_column_sql)
  rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
end
rename_table(table_name, new_name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 381
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) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
end
rollback_to_savepoint(name = current_savepoint_name) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 224
def rollback_to_savepoint(name = current_savepoint_name)
  log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { super }
end
show_variable(var) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 498
def show_variable(var)
  res = execute("show variables like '#{var}'")
  result_row = res.detect {|row| row["Variable_name"] == var }
  result_row && result_row["Value"]
end
strict_mode?() click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 71
def strict_mode? # strict_mode is default since AR 4.0
  config.key?(:strict) ?
    self.class.type_cast_config_to_boolean(config[:strict]) :
      ::ActiveRecord::VERSION::MAJOR > 3
end
structure_dump() click to toggle source

@deprecated no longer used - handled with (AR built-in) Rake tasks

# File lib/arjdbc/mysql/adapter.rb, line 251
def structure_dump
  # NOTE: due AR (2.3-3.2) compatibility views are not included
  if supports_views?
    sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
  else
    sql = "SHOW TABLES"
  end

  @connection.execute_query_raw(sql).map do |table|
    # e.g. { "Tables_in_arjdbc_test"=>"big_fields", "Table_type"=>"BASE TABLE" }
    table.delete('Table_type')
    table_name = table.to_a.first.last

    create_table = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")

    "#{create_table['Create Table']};\n\n"
  end.join
end
supports_index_sort_order?() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 187
def supports_index_sort_order?
  # Technically MySQL allows to create indexes with the sort order syntax
  # but at the moment (5.5) it doesn't yet implement them.
  true
end
supports_migrations?() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 177
def supports_migrations?
  true
end
supports_primary_key?() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 182
def supports_primary_key?
  true
end
supports_savepoints?() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 214
def supports_savepoints?
  true
end
supports_transaction_isolation?() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 194
def supports_transaction_isolation?
  # MySQL 4 technically support transaction isolation, but it is affected by
  # a bug where the transaction level gets persisted for the whole session:
  # http://bugs.mysql.com/bug.php?id=39170
  version[0] && version[0] >= 5
end
supports_views?() click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 202
def supports_views?
  version[0] && version[0] >= 5
end
type_to_sql(type, limit = nil, precision = nil, scale = nil) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 512
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
update_sql(sql, name = nil) click to toggle source

@override make it public just like native MySQL adapter does

# File lib/arjdbc/mysql/adapter.rb, line 244
def update_sql(sql, name = nil)
  super
end

Protected Instance Methods

quoted_columns_for_index(column_names, options = {}) click to toggle source
# File lib/arjdbc/mysql/adapter.rb, line 549
def quoted_columns_for_index(column_names, options = {})
  length = options[:length] if options.is_a?(Hash)

  case length
  when Hash
    column_names.map { |name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
  when Fixnum
    column_names.map { |name| "#{quote_column_name(name)}(#{length})" }
  else
    column_names.map { |name| quote_column_name(name) }
  end
end
translate_exception(exception, message) click to toggle source

@override

# File lib/arjdbc/mysql/adapter.rb, line 563
def translate_exception(exception, message)
  return super unless exception.respond_to?(:errno)

  case exception.errno
  when 1062
    ::ActiveRecord::RecordNotUnique.new(message, exception)
  when 1452
    ::ActiveRecord::InvalidForeignKey.new(message, exception)
  else
    super
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.