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.

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 Attribute Summary

Attributes included from QueryCache

#query_cache, #query_cache_enabled

Instance Method Summary collapse

Methods inherited from AbstractAdapter

#current_savepoint_name, #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?, #transaction_joinable=, #verify!

Methods included from QueryCache

#cache, #clear_query_cache, #columns_with_query_cache, dirties_query_cache, included, #select_all_with_query_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, #outside_transaction?, #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.



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

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)


272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 272

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:



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

def adapter_name #:nodoc:
  ADAPTER_NAME
end

#add_limit_offset!(sql, options) ⇒ Object

:nodoc:



369
370
371
372
373
374
375
376
377
378
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 369

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:



339
340
341
342
343
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 339

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

#case_sensitive_equality_operatorObject



557
558
559
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 557

def case_sensitive_equality_operator
  "= BINARY"
end

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

:nodoc:



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

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:



480
481
482
483
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 480

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



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

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.



425
426
427
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 425

def charset
  show_variable 'character_set_database'
end

#collationObject

Returns the database collation strategy.



430
431
432
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 430

def collation
  show_variable 'collation_database'
end

#columns(table_name, name = nil) ⇒ Object

:nodoc:



463
464
465
466
467
468
469
470
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 463

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

#commit_db_transactionObject

:nodoc:



345
346
347
348
349
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 345

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


408
409
410
411
412
413
414
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 408

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_savepointObject



357
358
359
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 357

def create_savepoint
  execute("SAVEPOINT #{current_savepoint_name}")
end

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

:nodoc:



472
473
474
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 472

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

#current_databaseObject



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

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

#disable_referential_integrity(&block) ⇒ Object

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



259
260
261
262
263
264
265
266
267
268
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 259

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



294
295
296
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 294

def disconnect!
  @connection.close rescue nil
end

#drop_database(name) ⇒ Object

:nodoc:



416
417
418
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 416

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

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



442
443
444
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 442

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

#emulate_booleansObject

:singleton-method: 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


172
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 172

cattr_accessor :emulate_booleans

#execute(sql, name = nil) ⇒ Object

Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you’re done using it.



319
320
321
322
323
324
325
326
327
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 319

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:



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 446

def indexes(table_name, name = nil)#:nodoc:
  indexes = []
  current_index = nil
  result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
  result.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
  result.free
  indexes
end

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

:nodoc:



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

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



561
562
563
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 561

def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  where_sql
end

#native_database_typesObject

:nodoc:



219
220
221
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 219

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#pk_and_sequence_for(table) ⇒ Object

Returns a table’s primary key and belonging sequence.



547
548
549
550
551
552
553
554
555
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 547

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

#quote(value, column = nil) ⇒ Object

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



226
227
228
229
230
231
232
233
234
235
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 226

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:



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

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

#quote_string(string) ⇒ Object

:nodoc:



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

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

#quote_table_name(name) ⇒ Object

:nodoc:



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

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

#quoted_falseObject



253
254
255
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 253

def quoted_false
  QUOTED_FALSE
end

#quoted_trueObject



249
250
251
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 249

def quoted_true
  QUOTED_TRUE
end

#reconnect!Object



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

def reconnect!
  disconnect!
  connect
end

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

:nodoc:



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

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

#release_savepointObject



365
366
367
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 365

def release_savepoint
  execute("RELEASE SAVEPOINT #{current_savepoint_name}")
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



511
512
513
514
515
516
517
518
519
520
521
522
523
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 511

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



476
477
478
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 476

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

#reset!Object



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

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:



351
352
353
354
355
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 351

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

#rollback_to_savepointObject



361
362
363
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 361

def rollback_to_savepoint
  execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
end

#select_rows(sql, name = nil) ⇒ Object

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



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

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’



541
542
543
544
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 541

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

#structure_dumpObject

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



383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 383

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)


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

def supports_migrations? #:nodoc:
  true
end

#supports_savepoints?Boolean

:nodoc:

Returns:

  • (Boolean)


215
216
217
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 215

def supports_savepoints? #:nodoc:
  true
end

#tables(name = nil) ⇒ Object

:nodoc:



434
435
436
437
438
439
440
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 434

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

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

Maps logical Rails types to MySQL-specific data types.



526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 526

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:



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

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