Class: ActiveRecord::ConnectionAdapters::MysqlAdapter

Inherits:
AbstractAdapter show all
Defined in:
lib/active_record/connection_adapters/mysql_adapter.rb

Overview

The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with the faster C-based MySQL/Ruby adapter (available both as a gem and from www.tmtm.org/en/mysql/ruby/).

Options:

  • :host - Defaults to “localhost”.

  • :port - Defaults to 3306.

  • :socket - Defaults to “/tmp/mysql.sock”.

  • :username - Defaults to “root”

  • :password - Defaults to nothing.

  • :database - The name of the database. No default, must be provided.

  • :encoding - (Optional) Sets the client encoding by executing “SET NAMES <encoding>” after connection.

  • :reconnect - Defaults to false (See MySQL documentation: dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).

  • :sslca - Necessary to use MySQL with an SSL connection.

  • :sslkey - Necessary to use MySQL with an SSL connection.

  • :sslcert - Necessary to use MySQL with an SSL connection.

  • :sslcapath - Necessary to use MySQL with an SSL connection.

  • :sslcipher - Necessary to use MySQL with an SSL connection.

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 environment.rb file:

ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false

Constant Summary collapse

ADAPTER_NAME =
'MySQL'.freeze
LOST_CONNECTION_ERROR_MESSAGES =
[
"Server shutdown in progress",
"Broken pipe",
"Lost connection to MySQL server during query",
"MySQL server has gone away" ]
NATIVE_DATABASE_TYPES =
{
  :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
  :string      => { :name => "varchar", :limit => 255 },
  :text        => { :name => "text" },
  :integer     => { :name => "int", :limit => 4 },
  :float       => { :name => "float" },
  :decimal     => { :name => "decimal" },
  :datetime    => { :name => "datetime" },
  :timestamp   => { :name => "datetime" },
  :time        => { :name => "time" },
  :date        => { :name => "date" },
  :binary      => { :name => "blob" },
  :boolean     => { :name => "tinyint", :limit => 1 }
}

Instance Method Summary collapse

Methods inherited from AbstractAdapter

#decrement_open_transactions, #increment_open_transactions, #log_info, #open_transactions, #prefetch_primary_key?, #raw_connection, #requires_reloading?, #reset_runtime, #supports_count_distinct?, #supports_ddl_transactions?, #verify!

Methods included from QueryCache

#cache, #uncached

Methods included from Quoting

#quoted_date, #quoted_string_prefix

Methods included from DatabaseStatements

#add_limit!, #add_lock!, #default_sequence_name, #delete, #empty_insert_statement, #insert, #insert_fixture, #reset_sequence!, #select_all, #select_one, #select_value, #select_values, #transaction, #update

Methods included from SchemaStatements

#add_column, #add_column_options!, #add_index, #add_order_by_for_association_limiting!, #add_timestamps, #assume_migrated_upto_version, #change_table, #distinct, #dump_schema_information, #index_name, #initialize_schema_migrations_table, #remove_column, #remove_index, #remove_timestamps, #table_alias_for, #table_alias_length, #table_exists?

Constructor Details

#initialize(connection, logger, connection_options, config) ⇒ MysqlAdapter

Returns a new instance of MysqlAdapter.



195
196
197
198
199
200
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 195

def initialize(connection, logger, connection_options, config)
  super(connection, logger)
  @connection_options, @config = connection_options, config
  @quoted_column_names, @quoted_table_names = {}, {}
  connect
end

Instance Method Details

#active?Boolean

CONNECTION MANAGEMENT ====================================

Returns:

  • (Boolean)


263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 263

def active?
  if @connection.respond_to?(:stat)
    @connection.stat
  else
    @connection.query 'select 1'
  end

  # mysql-ruby doesn't raise an exception when stat fails.
  if @connection.respond_to?(:errno)
    @connection.errno.zero?
  else
    true
  end
rescue Mysql::Error
  false
end

#adapter_nameObject

:nodoc:



202
203
204
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 202

def adapter_name #:nodoc:
  ADAPTER_NAME
end

#add_limit_offset!(sql, options) ⇒ Object

:nodoc:



348
349
350
351
352
353
354
355
356
357
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 348

def add_limit_offset!(sql, options) #:nodoc:
  if limit = options[:limit]
    limit = sanitize_limit(limit)
    unless offset = options[:offset]
      sql << " LIMIT #{limit}"
    else
      sql << " LIMIT #{offset.to_i}, #{limit}"
    end
  end
end

#begin_db_transactionObject

:nodoc:



329
330
331
332
333
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 329

def begin_db_transaction #:nodoc:
  execute "BEGIN"
rescue Exception
  # Transactions aren't supported
end

#case_sensitive_equality_operatorObject



528
529
530
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 528

def case_sensitive_equality_operator
  "= BINARY"
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object

:nodoc:



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 468

def change_column(table_name, column_name, type, options = {}) #:nodoc:
  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

  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)
  execute(change_column_sql)
end

#change_column_default(table_name, column_name, default) ⇒ Object

:nodoc:



453
454
455
456
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 453

def change_column_default(table_name, column_name, default) #:nodoc:
  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) ⇒ Object



458
459
460
461
462
463
464
465
466
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 458

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

#charsetObject

Returns the database character set.



404
405
406
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 404

def charset
  show_variable 'character_set_database'
end

#collationObject

Returns the database collation strategy.



409
410
411
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 409

def collation
  show_variable 'collation_database'
end

#columns(table_name, name = nil) ⇒ Object

:nodoc:



438
439
440
441
442
443
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 438

def columns(table_name, name = nil)#:nodoc:
  sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
  columns = []
  execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
  columns
end

#commit_db_transactionObject

:nodoc:



335
336
337
338
339
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 335

def commit_db_transaction #:nodoc:
  execute "COMMIT"
rescue Exception
  # Transactions aren't supported
end

#create_database(name, options = {}) ⇒ Object

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


387
388
389
390
391
392
393
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 387

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_table(table_name, options = {}) ⇒ Object

:nodoc:



445
446
447
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 445

def create_table(table_name, options = {}) #:nodoc:
  super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
end

#current_databaseObject



399
400
401
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 399

def current_database
  select_value 'SELECT DATABASE() as db'
end

#disable_referential_integrity(&block) ⇒ Object

REFERENTIAL INTEGRITY ====================================



250
251
252
253
254
255
256
257
258
259
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 250

def disable_referential_integrity(&block) #:nodoc:
  old = select_value("SELECT @@FOREIGN_KEY_CHECKS")

  begin
    update("SET FOREIGN_KEY_CHECKS = 0")
    yield
  ensure
    update("SET FOREIGN_KEY_CHECKS = #{old}")
  end
end

#disconnect!Object



285
286
287
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 285

def disconnect!
  @connection.close rescue nil
end

#drop_database(name) ⇒ Object

:nodoc:



395
396
397
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 395

def drop_database(name) #:nodoc:
  execute "DROP DATABASE IF EXISTS `#{name}`"
end

#drop_table(table_name, options = {}) ⇒ Object



419
420
421
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 419

def drop_table(table_name, options = {})
  super(table_name, options)
end

#execute(sql, name = nil) ⇒ Object

:nodoc:



309
310
311
312
313
314
315
316
317
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 309

def execute(sql, name = nil) #:nodoc:
  log(sql, name) { @connection.query(sql) }
rescue ActiveRecord::StatementInvalid => exception
  if exception.message.split(":").first =~ /Packets out of order/
    raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information.  If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
  else
    raise
  end
end

#indexes(table_name, name = nil) ⇒ Object

:nodoc:



423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 423

def indexes(table_name, name = nil)#:nodoc:
  indexes = []
  current_index = nil
  execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row|
    if current_index != row[2]
      next if row[2] == "PRIMARY" # skip the primary key
      current_index = row[2]
      indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
    end

    indexes.last.columns << row[4]
  end
  indexes
end

#insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object

:nodoc:



319
320
321
322
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 319

def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  super sql, name
  id_value || @connection.insert_id
end

#limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) ⇒ Object



532
533
534
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 532

def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  where_sql
end

#native_database_typesObject

:nodoc:



210
211
212
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 210

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#pk_and_sequence_for(table) ⇒ Object

Returns a table’s primary key and belonging sequence.



520
521
522
523
524
525
526
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 520

def pk_and_sequence_for(table) #:nodoc:
  keys = []
  execute("describe #{quote_table_name(table)}").each_hash do |h|
    keys << h["Field"]if h["Key"] == "PRI"
  end
  keys.length == 1 ? [keys.first, nil] : nil
end

#quote(value, column = nil) ⇒ Object

QUOTING ==================================================



217
218
219
220
221
222
223
224
225
226
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 217

def quote(value, column = nil)
  if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
    s = column.class.string_to_binary(value).unpack("H*")[0]
    "x'#{s}'"
  elsif value.kind_of?(BigDecimal)
    value.to_s("F")
  else
    super
  end
end

#quote_column_name(name) ⇒ Object

:nodoc:



228
229
230
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 228

def quote_column_name(name) #:nodoc:
  @quoted_column_names[name] ||= "`#{name}`"
end

#quote_string(string) ⇒ Object

:nodoc:



236
237
238
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 236

def quote_string(string) #:nodoc:
  @connection.quote(string)
end

#quote_table_name(name) ⇒ Object

:nodoc:



232
233
234
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 232

def quote_table_name(name) #:nodoc:
  @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
end

#quoted_falseObject



244
245
246
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 244

def quoted_false
  QUOTED_FALSE
end

#quoted_trueObject



240
241
242
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 240

def quoted_true
  QUOTED_TRUE
end

#reconnect!Object



280
281
282
283
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 280

def reconnect!
  disconnect!
  connect
end

#recreate_database(name, options = {}) ⇒ Object

:nodoc:



375
376
377
378
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 375

def recreate_database(name, options = {}) #:nodoc:
  drop_database(name)
  create_database(name, options)
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 484

def rename_column(table_name, column_name, new_column_name) #:nodoc:
  options = {}
  if column = columns(table_name).find { |c| c.name == column_name.to_s }
    options[:default] = column.default
    options[:null] = column.null
  else
    raise 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)
end

#rename_table(table_name, new_name) ⇒ Object



449
450
451
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 449

def rename_table(table_name, new_name)
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
end

#reset!Object



289
290
291
292
293
294
295
296
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 289

def reset!
  if @connection.respond_to?(:change_user)
    # See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
    # reset the connection is to change the user to the same user.
    @connection.change_user(@config[:username], @config[:password], @config[:database])
    configure_connection
  end
end

#rollback_db_transactionObject

:nodoc:



341
342
343
344
345
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 341

def rollback_db_transaction #:nodoc:
  execute "ROLLBACK"
rescue Exception
  # Transactions aren't supported
end

#select_rows(sql, name = nil) ⇒ Object

DATABASE STATEMENTS ======================================



300
301
302
303
304
305
306
307
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 300

def select_rows(sql, name = nil)
  @connection.query_with_result = true
  result = execute(sql, name)
  rows = []
  result.each { |row| rows << row }
  result.free
  rows
end

#show_variable(name) ⇒ Object

SHOW VARIABLES LIKE ‘name’



514
515
516
517
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 514

def show_variable(name)
  variables = select_all("SHOW VARIABLES LIKE '#{name}'")
  variables.first['Value'] unless variables.empty?
end

#structure_dumpObject

SCHEMA STATEMENTS ========================================



362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 362

def structure_dump #:nodoc:
  if supports_views?
    sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
  else
    sql = "SHOW TABLES"
  end

  select_all(sql).inject("") do |structure, table|
    table.delete('Table_type')
    structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
  end
end

#supports_migrations?Boolean

:nodoc:

Returns:

  • (Boolean)


206
207
208
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 206

def supports_migrations? #:nodoc:
  true
end

#tables(name = nil) ⇒ Object

:nodoc:



413
414
415
416
417
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 413

def tables(name = nil) #:nodoc:
  tables = []
  execute("SHOW TABLES", name).each { |field| tables << field[0] }
  tables
end

#type_to_sql(type, limit = nil, precision = nil, scale = nil) ⇒ Object

Maps logical Rails types to MySQL-specific data types.



499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 499

def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  return super unless type.to_s == '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
end

#update_sql(sql, name = nil) ⇒ Object

:nodoc:



324
325
326
327
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 324

def update_sql(sql, name = nil) #:nodoc:
  super
  @connection.affected_rows
end