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.

  • :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

LOST_CONNECTION_ERROR_MESSAGES =
[
"Server shutdown in progress",
"Broken pipe",
"Lost connection to MySQL server during query",
"MySQL server has gone away" ]
@@emulate_booleans =
true

Instance Method Summary collapse

Methods inherited from AbstractAdapter

#log_info, #prefetch_primary_key?, #raw_connection, #requires_reloading?, #reset_runtime, #supports_count_distinct?, #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!, #sanitize_limit, #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.



182
183
184
185
186
187
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 182

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:



189
190
191
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 189

def adapter_name #:nodoc:
  'MySQL'
end

#add_limit_offset!(sql, options) ⇒ Object

:nodoc:



340
341
342
343
344
345
346
347
348
349
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 340

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:



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

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

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

:nodoc:



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 460

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:



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

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



450
451
452
453
454
455
456
457
458
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 450

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.



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

def charset
  show_variable 'character_set_database'
end

#collationObject

Returns the database collation strategy.



401
402
403
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 401

def collation
  show_variable 'collation_database'
end

#columns(table_name, name = nil) ⇒ Object

:nodoc:



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

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:



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

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


379
380
381
382
383
384
385
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 379

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:



437
438
439
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 437

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

#current_databaseObject



391
392
393
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 391

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:



387
388
389
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 387

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

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



411
412
413
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 411

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

#execute(sql, name = nil) ⇒ Object

:nodoc:



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

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:



415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 415

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:



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

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

#native_database_typesObject

:nodoc:



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 197

def native_database_types #:nodoc:
  {
    :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 }
  }
end

#pk_and_sequence_for(table) ⇒ Object

Returns a table’s primary key and belonging sequence.



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

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) ⇒ Object

:nodoc:



367
368
369
370
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 367

def recreate_database(name) #:nodoc:
  drop_database(name)
  create_database(name)
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 476

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



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

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

#rollback_db_transactionObject

:nodoc:



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

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

#select_rows(sql, name = nil) ⇒ Object

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



292
293
294
295
296
297
298
299
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 292

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’



506
507
508
509
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 506

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

#structure_dumpObject

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



354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 354

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)


193
194
195
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 193

def supports_migrations? #:nodoc:
  true
end

#tables(name = nil) ⇒ Object

:nodoc:



405
406
407
408
409
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 405

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.



491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 491

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:



316
317
318
319
# File 'lib/active_record/connection_adapters/mysql_adapter.rb', line 316

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