Module: ArJdbc::MySQL

Included in:
ActiveRecord::ConnectionAdapters::MysqlAdapter
Defined in:
lib/arjdbc/mysql/adapter.rb,
lib/arjdbc/mysql/explain_support.rb

Defined Under Namespace

Modules: Column, ExplainSupport

Constant Summary collapse

ColumnExtensions =

:nodoc: backwards-compatibility

Column
NATIVE_DATABASE_TYPES =
{
  :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
  :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 }
}
ADAPTER_NAME =
'MySQL'.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.arel2_visitors(config) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/arjdbc/mysql/adapter.rb', line 131

def self.arel2_visitors(config)
  {
    'mysql' => ::Arel::Visitors::MySQL,
    'mysql2' => ::Arel::Visitors::MySQL,
    'jdbcmysql' => ::Arel::Visitors::MySQL
  }
end

.column_selectorObject



16
17
18
# File 'lib/arjdbc/mysql/adapter.rb', line 16

def self.column_selector
  [ /mysql/i, lambda { |_,column| column.extend(::ArJdbc::MySQL::Column) } ]
end

.extended(adapter) ⇒ Object



8
9
10
# File 'lib/arjdbc/mysql/adapter.rb', line 8

def self.extended(adapter)
  adapter.configure_connection
end

.jdbc_connection_classObject



20
21
22
# File 'lib/arjdbc/mysql/adapter.rb', line 20

def self.jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
end

Instance Method Details

#adapter_nameObject

:nodoc:



127
128
129
# File 'lib/arjdbc/mysql/adapter.rb', line 127

def adapter_name #:nodoc:
  ADAPTER_NAME
end

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



348
349
350
351
352
353
# File 'lib/arjdbc/mysql/adapter.rb', line 348

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



481
482
483
484
485
486
487
# File 'lib/arjdbc/mysql/adapter.rb', line 481

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

:nodoc:



401
402
403
404
405
406
407
408
409
410
411
# File 'lib/arjdbc/mysql/adapter.rb', line 401

def add_limit_offset!(sql, options) #:nodoc:
  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_operatorObject



139
140
141
# File 'lib/arjdbc/mysql/adapter.rb', line 139

def case_sensitive_equality_operator
  "= BINARY"
end

#case_sensitive_modifier(node) ⇒ Object



143
144
145
# File 'lib/arjdbc/mysql/adapter.rb', line 143

def case_sensitive_modifier(node)
  Arel::Nodes::Bin.new(node)
end

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

:nodoc:



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/arjdbc/mysql/adapter.rb', line 370

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)
  add_column_position!(change_column_sql, options)
  execute(change_column_sql)
end

#change_column_default(table_name, column_name, default) ⇒ Object

:nodoc:



355
356
357
358
# File 'lib/arjdbc/mysql/adapter.rb', line 355

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



360
361
362
363
364
365
366
367
368
# File 'lib/arjdbc/mysql/adapter.rb', line 360

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



442
443
444
# File 'lib/arjdbc/mysql/adapter.rb', line 442

def charset
  show_variable("character_set_database")
end

#collationObject



446
447
448
# File 'lib/arjdbc/mysql/adapter.rb', line 446

def collation
  show_variable("collation_database")
end

#configure_connectionObject



12
13
14
# File 'lib/arjdbc/mysql/adapter.rb', line 12

def configure_connection
  execute("SET SQL_AUTO_IS_NULL=0")
end

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

:nodoc:



319
320
321
322
323
324
325
# File 'lib/arjdbc/mysql/adapter.rb', line 319

def create_database(name, options = {}) #:nodoc:
  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



209
210
211
# File 'lib/arjdbc/mysql/adapter.rb', line 209

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

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

:nodoc:



335
336
337
# File 'lib/arjdbc/mysql/adapter.rb', line 335

def create_table(name, options = {}) #:nodoc:
  super(name, {:options => "ENGINE=InnoDB DEFAULT CHARSET=utf8"}.merge(options))
end

#current_databaseObject



331
332
333
# File 'lib/arjdbc/mysql/adapter.rb', line 331

def current_database
  select_one("SELECT DATABASE() as db")["db"]
end

#disable_referential_integrityObject

:nodoc:



221
222
223
224
225
226
227
228
229
# File 'lib/arjdbc/mysql/adapter.rb', line 221

def disable_referential_integrity # :nodoc:
  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) ⇒ Object

:nodoc:



327
328
329
# File 'lib/arjdbc/mysql/adapter.rb', line 327

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

#exec_insert(sql, name, binds) ⇒ Object Also known as: exec_update, exec_delete

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



233
234
235
# File 'lib/arjdbc/mysql/adapter.rb', line 233

def exec_insert(sql, name, binds)
  execute sql, name, binds
end

#indexes(table_name, name = nil) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/arjdbc/mysql/adapter.rb', line 282

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|
    key_name = row["Key_name"]
    if current_index != key_name
      next if key_name == "PRIMARY" # skip the primary key
      current_index = key_name
      indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(
        row["Table"], key_name, row["Non_unique"] == 0, [], [])
    end

    indexes.last.columns << row["Column_name"]
    indexes.last.lengths << row["Sub_part"]
  end
  indexes
end

#jdbc_columns(table_name, name = nil) ⇒ Object

:nodoc:



301
302
303
304
305
306
# File 'lib/arjdbc/mysql/adapter.rb', line 301

def jdbc_columns(table_name, name = nil)#:nodoc:
  sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
  execute(sql, 'SCHEMA').map do |field|
    ::ActiveRecord::ConnectionAdapters::MysqlColumn.new(field["Field"], field["Default"], field["Type"], field["Null"] == "YES")
  end
end

#join_to_update(update, select) ⇒ Object

Taken from: github.com/gfmurphy/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L540

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!



420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/arjdbc/mysql/adapter.rb', line 420

def join_to_update(update, select) #:nodoc:
  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) ⇒ Object



147
148
149
# File 'lib/arjdbc/mysql/adapter.rb', line 147

def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  where_sql
end

#modify_types(types) ⇒ Object



116
117
118
119
120
121
122
123
# File 'lib/arjdbc/mysql/adapter.rb', line 116

def modify_types(types)
  types[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
  types[:integer] = { :name => 'int', :limit => 4 }
  types[:decimal] = { :name => "decimal" }
  types[:timestamp] = { :name => "datetime" }
  types[:datetime][:limit] = nil
  types
end

#native_database_typesObject



112
113
114
# File 'lib/arjdbc/mysql/adapter.rb', line 112

def native_database_types
  NATIVE_DATABASE_TYPES
end

#pk_and_sequence_for(table) ⇒ Object

based on: github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L756 Required for passing rails column caching tests Returns a table’s primary key and belonging sequence.



270
271
272
273
274
275
276
277
# File 'lib/arjdbc/mysql/adapter.rb', line 270

def pk_and_sequence_for(table) #:nodoc:
  keys = []
  result = execute("SHOW INDEX FROM #{quote_table_name(table)} WHERE Key_name = 'PRIMARY'", 'SCHEMA')
  result.each do |h|
    keys << h["Column_name"]
  end
  keys.length == 1 ? [keys.first, nil] : nil
end

#primary_key(table) ⇒ Object

Returns just a table’s primary key



309
310
311
312
# File 'lib/arjdbc/mysql/adapter.rb', line 309

def primary_key(table)
  pk_and_sequence = pk_and_sequence_for(table)
  pk_and_sequence && pk_and_sequence.first
end

#quote(value, column = nil) ⇒ Object

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



153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/arjdbc/mysql/adapter.rb', line 153

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

#quote_column_name(name) ⇒ Object

:nodoc:



166
167
168
# File 'lib/arjdbc/mysql/adapter.rb', line 166

def quote_column_name(name) # :nodoc:
  "`#{name.to_s.gsub('`', '``')}`"
end

#quote_table_name(name) ⇒ Object

:nodoc:



170
171
172
# File 'lib/arjdbc/mysql/adapter.rb', line 170

def quote_table_name(name) # :nodoc:
  quote_column_name(name).gsub('.', '`.`')
end

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

:nodoc:



314
315
316
317
# File 'lib/arjdbc/mysql/adapter.rb', line 314

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

#release_savepointObject



217
218
219
# File 'lib/arjdbc/mysql/adapter.rb', line 217

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

#remove_index!(table_name, index_name) ⇒ Object

:nodoc:



343
344
345
346
# File 'lib/arjdbc/mysql/adapter.rb', line 343

def remove_index!(table_name, index_name) #:nodoc:
  # 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) ⇒ Object

:nodoc:



387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/arjdbc/mysql/adapter.rb', line 387

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 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)
end

#rename_table(name, new_name) ⇒ Object



339
340
341
# File 'lib/arjdbc/mysql/adapter.rb', line 339

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

#rollback_to_savepointObject



213
214
215
# File 'lib/arjdbc/mysql/adapter.rb', line 213

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

#show_variable(var) ⇒ Object



436
437
438
439
440
# File 'lib/arjdbc/mysql/adapter.rb', line 436

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

#structure_dumpObject

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



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/arjdbc/mysql/adapter.rb', line 246

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')

    hash = show_create_table(table.to_a.first.last)

    if(table = hash["Create Table"])
      structure += table + ";\n\n"
    elsif(view = hash["Create View"])
      structure += view + ";\n\n"
    end
  end
end

#supports_bulk_alter?Boolean

:nodoc:

Returns:

  • (Boolean)


183
184
185
# File 'lib/arjdbc/mysql/adapter.rb', line 183

def supports_bulk_alter? # :nodoc:
  true
end

#supports_index_sort_order?Boolean

Technically MySQL allows to create indexes with the sort order syntax but at the moment (5.5) it doesn’t yet implement them

Returns:

  • (Boolean)


189
190
191
# File 'lib/arjdbc/mysql/adapter.rb', line 189

def supports_index_sort_order? # :nodoc:
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)


175
176
177
# File 'lib/arjdbc/mysql/adapter.rb', line 175

def supports_migrations?
  true
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)


179
180
181
# File 'lib/arjdbc/mysql/adapter.rb', line 179

def supports_primary_key? # :nodoc:
  true
end

#supports_savepoints?Boolean

:nodoc:

Returns:

  • (Boolean)


205
206
207
# File 'lib/arjdbc/mysql/adapter.rb', line 205

def supports_savepoints? # :nodoc:
  true
end

#supports_transaction_isolation?Boolean

MySQL 4 technically support transaction isolation, but it is affected by a bug where the transaction level gets persisted for the whole session:

bugs.mysql.com/bug.php?id=39170

Returns:

  • (Boolean)


197
198
199
# File 'lib/arjdbc/mysql/adapter.rb', line 197

def supports_transaction_isolation? # :nodoc:
  version[0] && version[0] >= 5
end

#supports_views?Boolean

:nodoc:

Returns:

  • (Boolean)


201
202
203
# File 'lib/arjdbc/mysql/adapter.rb', line 201

def supports_views? # :nodoc:
  version[0] && version[0] >= 5
end

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



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/arjdbc/mysql/adapter.rb', line 450

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

Make it public just like native MySQL adapter does.



240
241
242
# File 'lib/arjdbc/mysql/adapter.rb', line 240

def update_sql(sql, name = nil) # :nodoc:
  super
end