mysql2 0.2.24 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/CHANGELOG.md +148 -0
  5. data/Gemfile +3 -0
  6. data/README.rdoc +257 -0
  7. data/Rakefile +5 -0
  8. data/benchmark/active_record.rb +51 -0
  9. data/benchmark/active_record_threaded.rb +42 -0
  10. data/benchmark/allocations.rb +33 -0
  11. data/benchmark/escape.rb +36 -0
  12. data/benchmark/query_with_mysql_casting.rb +80 -0
  13. data/benchmark/query_without_mysql_casting.rb +47 -0
  14. data/benchmark/sequel.rb +37 -0
  15. data/benchmark/setup_db.rb +119 -0
  16. data/benchmark/threaded.rb +44 -0
  17. data/ext/mysql2/client.c +272 -849
  18. data/ext/mysql2/client.h +12 -27
  19. data/ext/mysql2/extconf.rb +14 -72
  20. data/ext/mysql2/mysql2_ext.h +4 -7
  21. data/ext/mysql2/result.c +123 -319
  22. data/ext/mysql2/result.h +1 -4
  23. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +64 -0
  24. data/lib/active_record/fiber_patches.rb +104 -0
  25. data/lib/mysql2.rb +5 -20
  26. data/lib/mysql2/client.rb +200 -50
  27. data/lib/mysql2/em.rb +3 -13
  28. data/lib/mysql2/em_fiber.rb +31 -0
  29. data/lib/mysql2/error.rb +6 -71
  30. data/lib/mysql2/version.rb +2 -2
  31. data/mysql2.gemspec +32 -0
  32. data/spec/em/em_fiber_spec.rb +22 -0
  33. data/spec/em/em_spec.rb +9 -74
  34. data/spec/mysql2/client_spec.rb +126 -593
  35. data/spec/mysql2/error_spec.rb +44 -58
  36. data/spec/mysql2/result_spec.rb +85 -257
  37. data/spec/spec_helper.rb +3 -24
  38. data/tasks/benchmarks.rake +20 -0
  39. data/tasks/compile.rake +71 -0
  40. data/tasks/rspec.rake +16 -0
  41. data/tasks/vendor_mysql.rake +40 -0
  42. metadata +179 -92
  43. checksums.yaml +0 -7
  44. data/README.md +0 -524
  45. data/ext/mysql2/infile.c +0 -122
  46. data/ext/mysql2/infile.h +0 -1
  47. data/ext/mysql2/mysql_enc_name_to_ruby.h +0 -168
  48. data/ext/mysql2/mysql_enc_to_ruby.h +0 -246
  49. data/ext/mysql2/wait_for_single_fd.h +0 -36
  50. data/lib/active_record/connection_adapters/mysql2_adapter.rb +0 -635
  51. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +0 -11
  52. data/lib/mysql2/console.rb +0 -5
  53. data/spec/configuration.yml.example +0 -17
  54. data/spec/my.cnf.example +0 -9
  55. data/spec/test_data +0 -1
  56. data/support/mysql_enc_to_ruby.rb +0 -82
  57. data/support/ruby_enc_to_mysql.rb +0 -61
@@ -1,36 +0,0 @@
1
- /*
2
- * backwards compatibility for pre-1.9.3 C API
3
- *
4
- * Ruby 1.9.3 provides this API which allows the use of ppoll() on Linux
5
- * to minimize select() and malloc() overhead on high-numbered FDs.
6
- */
7
- #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
8
- # include <ruby/io.h>
9
- #else
10
- # define RB_WAITFD_IN 0x001
11
- # define RB_WAITFD_PRI 0x002
12
- # define RB_WAITFD_OUT 0x004
13
-
14
- static int my_wait_for_single_fd(int fd, int events, struct timeval *tvp)
15
- {
16
- fd_set fdset;
17
- fd_set *rfds = NULL;
18
- fd_set *wfds = NULL;
19
- fd_set *efds = NULL;
20
-
21
- FD_ZERO(&fdset);
22
- FD_SET(fd, &fdset);
23
-
24
- if (events & RB_WAITFD_IN)
25
- rfds = &fdset;
26
- if (events & RB_WAITFD_OUT)
27
- wfds = &fdset;
28
- if (events & RB_WAITFD_PRI)
29
- efds = &fdset;
30
-
31
- return rb_thread_select(fd + 1, rfds, wfds, efds, tvp);
32
- }
33
-
34
- #define rb_wait_for_single_fd(fd,events,tvp) \
35
- my_wait_for_single_fd((fd),(events),(tvp))
36
- #endif
@@ -1,635 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'mysql2'
4
-
5
- module ActiveRecord
6
- class Base
7
- def self.mysql2_connection(config)
8
- config = config.symbolize_keys
9
-
10
- config[:username] = 'root' if config[:username].nil?
11
-
12
- if Mysql2::Client.const_defined? :FOUND_ROWS
13
- config[:flags] = Mysql2::Client::FOUND_ROWS
14
- end
15
-
16
- client = Mysql2::Client.new(config)
17
- options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
18
- ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
19
- end
20
- end
21
-
22
- module ConnectionAdapters
23
- class Mysql2IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
24
- end
25
-
26
- class Mysql2Column < Column
27
- BOOL = "tinyint(1)"
28
- def extract_default(default)
29
- if sql_type =~ /blob/i || type == :text
30
- if default.blank?
31
- return null ? nil : ''
32
- else
33
- raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
34
- end
35
- elsif missing_default_forged_as_empty_string?(default)
36
- nil
37
- else
38
- super
39
- end
40
- end
41
-
42
- def has_default?
43
- return false if sql_type =~ /blob/i || type == :text # mysql forbids defaults on blob and text columns
44
- super
45
- end
46
-
47
- private
48
- def simplified_type(field_type)
49
- return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL)
50
- return :string if field_type =~ /enum/i or field_type =~ /set/i
51
- return :integer if field_type =~ /year/i
52
- return :binary if field_type =~ /bit/i
53
- super
54
- end
55
-
56
- def extract_limit(sql_type)
57
- case sql_type
58
- when /blob|text/i
59
- case sql_type
60
- when /tiny/i
61
- 255
62
- when /medium/i
63
- 16777215
64
- when /long/i
65
- 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
66
- else
67
- super # we could return 65535 here, but we leave it undecorated by default
68
- end
69
- when /^bigint/i; 8
70
- when /^int/i; 4
71
- when /^mediumint/i; 3
72
- when /^smallint/i; 2
73
- when /^tinyint/i; 1
74
- else
75
- super
76
- end
77
- end
78
-
79
- # MySQL misreports NOT NULL column default when none is given.
80
- # We can't detect this for columns which may have a legitimate ''
81
- # default (string) but we can for others (integer, datetime, boolean,
82
- # and the rest).
83
- #
84
- # Test whether the column has default '', is not null, and is not
85
- # a type allowing default ''.
86
- def missing_default_forged_as_empty_string?(default)
87
- type != :string && !null && default == ''
88
- end
89
- end
90
-
91
- class Mysql2Adapter < AbstractAdapter
92
- cattr_accessor :emulate_booleans
93
- self.emulate_booleans = true
94
-
95
- ADAPTER_NAME = 'Mysql2'
96
- PRIMARY = "PRIMARY"
97
-
98
- LOST_CONNECTION_ERROR_MESSAGES = [
99
- "Server shutdown in progress",
100
- "Broken pipe",
101
- "Lost connection to MySQL server during query",
102
- "MySQL server has gone away" ]
103
-
104
- QUOTED_TRUE, QUOTED_FALSE = '1', '0'
105
-
106
- NATIVE_DATABASE_TYPES = {
107
- :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
108
- :string => { :name => "varchar", :limit => 255 },
109
- :text => { :name => "text" },
110
- :integer => { :name => "int", :limit => 4 },
111
- :float => { :name => "float" },
112
- :decimal => { :name => "decimal" },
113
- :datetime => { :name => "datetime" },
114
- :timestamp => { :name => "datetime" },
115
- :time => { :name => "time" },
116
- :date => { :name => "date" },
117
- :binary => { :name => "blob" },
118
- :boolean => { :name => "tinyint", :limit => 1 }
119
- }
120
-
121
- def initialize(connection, logger, connection_options, config)
122
- super(connection, logger)
123
- @connection_options, @config = connection_options, config
124
- @quoted_column_names, @quoted_table_names = {}, {}
125
- configure_connection
126
- end
127
-
128
- def adapter_name
129
- ADAPTER_NAME
130
- end
131
-
132
- def supports_migrations?
133
- true
134
- end
135
-
136
- def supports_primary_key?
137
- true
138
- end
139
-
140
- def supports_savepoints?
141
- true
142
- end
143
-
144
- def native_database_types
145
- NATIVE_DATABASE_TYPES
146
- end
147
-
148
- # QUOTING ==================================================
149
-
150
- def quote(value, column = nil)
151
- if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
152
- s = column.class.string_to_binary(value).unpack("H*")[0]
153
- "x'#{s}'"
154
- else
155
- super
156
- end
157
- end
158
-
159
- def quote_column_name(name) #:nodoc:
160
- @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
161
- end
162
-
163
- def quote_table_name(name) #:nodoc:
164
- @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
165
- end
166
-
167
- def quote_string(string)
168
- @connection.escape(string)
169
- end
170
-
171
- def quoted_true
172
- QUOTED_TRUE
173
- end
174
-
175
- def quoted_false
176
- QUOTED_FALSE
177
- end
178
-
179
- # REFERENTIAL INTEGRITY ====================================
180
-
181
- def disable_referential_integrity(&block) #:nodoc:
182
- old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
183
-
184
- begin
185
- update("SET FOREIGN_KEY_CHECKS = 0")
186
- yield
187
- ensure
188
- update("SET FOREIGN_KEY_CHECKS = #{old}")
189
- end
190
- end
191
-
192
- # CONNECTION MANAGEMENT ====================================
193
-
194
- def active?
195
- return false unless @connection
196
- @connection.ping
197
- end
198
-
199
- def reconnect!
200
- disconnect!
201
- connect
202
- end
203
-
204
- # this is set to true in 2.3, but we don't want it to be
205
- def requires_reloading?
206
- false
207
- end
208
-
209
- def disconnect!
210
- unless @connection.nil?
211
- @connection.close
212
- @connection = nil
213
- end
214
- end
215
-
216
- def reset!
217
- disconnect!
218
- connect
219
- end
220
-
221
- # DATABASE STATEMENTS ======================================
222
-
223
- # FIXME: re-enable the following once a "better" query_cache solution is in core
224
- #
225
- # The overrides below perform much better than the originals in AbstractAdapter
226
- # because we're able to take advantage of mysql2's lazy-loading capabilities
227
- #
228
- # # Returns a record hash with the column names as keys and column values
229
- # # as values.
230
- # def select_one(sql, name = nil)
231
- # result = execute(sql, name)
232
- # result.each(:as => :hash) do |r|
233
- # return r
234
- # end
235
- # end
236
- #
237
- # # Returns a single value from a record
238
- # def select_value(sql, name = nil)
239
- # result = execute(sql, name)
240
- # if first = result.first
241
- # first.first
242
- # end
243
- # end
244
- #
245
- # # Returns an array of the values of the first column in a select:
246
- # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
247
- # def select_values(sql, name = nil)
248
- # execute(sql, name).map { |row| row.first }
249
- # end
250
-
251
- # Returns an array of arrays containing the field values.
252
- # Order is the same as that returned by +columns+.
253
- def select_rows(sql, name = nil)
254
- execute(sql, name).to_a
255
- end
256
-
257
- # Executes the SQL statement in the context of this connection.
258
- def execute(sql, name = nil)
259
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
260
- # made since we established the connection
261
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
262
- if name == :skip_logging
263
- @connection.query(sql)
264
- else
265
- log(sql, name) { @connection.query(sql) }
266
- end
267
- rescue ActiveRecord::StatementInvalid => exception
268
- if exception.message.split(":").first =~ /Packets out of order/
269
- 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."
270
- else
271
- raise
272
- end
273
- end
274
-
275
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
276
- super
277
- id_value || @connection.last_id
278
- end
279
- alias :create :insert_sql
280
-
281
- def update_sql(sql, name = nil)
282
- super
283
- @connection.affected_rows
284
- end
285
-
286
- def begin_db_transaction
287
- execute "BEGIN"
288
- rescue Exception
289
- # Transactions aren't supported
290
- end
291
-
292
- def commit_db_transaction
293
- execute "COMMIT"
294
- rescue Exception
295
- # Transactions aren't supported
296
- end
297
-
298
- def rollback_db_transaction
299
- execute "ROLLBACK"
300
- rescue Exception
301
- # Transactions aren't supported
302
- end
303
-
304
- def create_savepoint
305
- execute("SAVEPOINT #{current_savepoint_name}")
306
- end
307
-
308
- def rollback_to_savepoint
309
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
310
- end
311
-
312
- def release_savepoint
313
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
314
- end
315
-
316
- def add_limit_offset!(sql, options)
317
- limit, offset = options[:limit], options[:offset]
318
- if limit && offset
319
- sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
320
- elsif limit
321
- sql << " LIMIT #{sanitize_limit(limit)}"
322
- elsif offset
323
- limit = 18446744073709551615 if limit.nil?
324
- sql << " LIMIT #{sanitize_limit(limit)} OFFSET #{offset.to_i}"
325
- end
326
- sql
327
- end
328
-
329
- # SCHEMA STATEMENTS ========================================
330
-
331
- def structure_dump
332
- if supports_views?
333
- sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
334
- else
335
- sql = "SHOW TABLES"
336
- end
337
-
338
- select_all(sql).inject("") do |structure, table|
339
- table.delete('Table_type')
340
- structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
341
- end
342
- end
343
-
344
- def recreate_database(name, options = {})
345
- drop_database(name)
346
- create_database(name, options)
347
- end
348
-
349
- # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
350
- # Charset defaults to utf8.
351
- #
352
- # Example:
353
- # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
354
- # create_database 'matt_development'
355
- # create_database 'matt_development', :charset => :big5
356
- def create_database(name, options = {})
357
- if options[:collation]
358
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
359
- else
360
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
361
- end
362
- end
363
-
364
- def drop_database(name) #:nodoc:
365
- execute "DROP DATABASE IF EXISTS `#{name}`"
366
- end
367
-
368
- def current_database
369
- select_value 'SELECT DATABASE() as db'
370
- end
371
-
372
- # Returns the database character set.
373
- def charset
374
- show_variable 'character_set_database'
375
- end
376
-
377
- # Returns the database collation strategy.
378
- def collation
379
- show_variable 'collation_database'
380
- end
381
-
382
- def tables(name = nil, database = nil)
383
- tables = []
384
-
385
- sql = "SHOW TABLES "
386
- sql << "IN #{quote_table_name(database)} " if database
387
-
388
- execute(sql, 'SCHEMA').each do |field|
389
- tables << field.first
390
- end
391
- tables
392
- end
393
-
394
- def table_exists?(name)
395
- return true if super
396
-
397
- name = name.to_s
398
- schema, table = name.split('.', 2)
399
-
400
- unless table # A table was provided without a schema
401
- table = schema
402
- schema = nil
403
- end
404
-
405
- tables(nil, schema).include? table
406
- end
407
-
408
- def drop_table(table_name, options = {})
409
- super(table_name, options)
410
- end
411
-
412
- def indexes(table_name, name = nil)
413
- indexes = []
414
- current_index = nil
415
- result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
416
- result.each(:symbolize_keys => true, :as => :hash) do |row|
417
- if current_index != row[:Key_name]
418
- next if row[:Key_name] == PRIMARY # skip the primary key
419
- current_index = row[:Key_name]
420
- indexes << Mysql2IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, [], [])
421
- end
422
-
423
- indexes.last.columns << row[:Column_name]
424
- indexes.last.lengths << row[:Sub_part]
425
- end
426
- indexes
427
- end
428
-
429
- def columns(table_name, name = nil)
430
- sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
431
- columns = []
432
- result = execute(sql, :skip_logging)
433
- result.each(:symbolize_keys => true, :as => :hash) { |field|
434
- columns << Mysql2Column.new(field[:Field], field[:Default], field[:Type], field[:Null] == "YES")
435
- }
436
- columns
437
- end
438
-
439
- def create_table(table_name, options = {})
440
- super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
441
- end
442
-
443
- def rename_table(table_name, new_name)
444
- execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
445
- end
446
-
447
- def add_column(table_name, column_name, type, options = {})
448
- 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])}"
449
- add_column_options!(add_column_sql, options)
450
- add_column_position!(add_column_sql, options)
451
- execute(add_column_sql)
452
- end
453
-
454
- def change_column_default(table_name, column_name, default)
455
- column = column_for(table_name, column_name)
456
- change_column table_name, column_name, column.sql_type, :default => default
457
- end
458
-
459
- def change_column_null(table_name, column_name, null, default = nil)
460
- column = column_for(table_name, column_name)
461
-
462
- unless null || default.nil?
463
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
464
- end
465
-
466
- change_column table_name, column_name, column.sql_type, :null => null
467
- end
468
-
469
- def change_column(table_name, column_name, type, options = {})
470
- column = column_for(table_name, column_name)
471
-
472
- unless options_include_default?(options)
473
- options[:default] = column.default
474
- end
475
-
476
- unless options.has_key?(:null)
477
- options[:null] = column.null
478
- end
479
-
480
- 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])}"
481
- add_column_options!(change_column_sql, options)
482
- add_column_position!(change_column_sql, options)
483
- execute(change_column_sql)
484
- end
485
-
486
- def rename_column(table_name, column_name, new_column_name)
487
- options = {}
488
- if column = columns(table_name).find { |c| c.name == column_name.to_s }
489
- options[:default] = column.default
490
- options[:null] = column.null
491
- else
492
- raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
493
- end
494
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
495
- rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
496
- add_column_options!(rename_column_sql, options)
497
- execute(rename_column_sql)
498
- end
499
-
500
- # Maps logical Rails types to MySQL-specific data types.
501
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
502
- case type.to_s
503
- when 'integer'
504
- case limit
505
- when 1; 'tinyint'
506
- when 2; 'smallint'
507
- when 3; 'mediumint'
508
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
509
- when 5..8; 'bigint'
510
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
511
- end
512
- when 'text'
513
- case limit
514
- when 0..0xff; 'tinytext'
515
- when nil, 0x100..0xffff; 'text'
516
- when 0x10000..0xffffff; 'mediumtext'
517
- when 0x1000000..0xffffffff; 'longtext'
518
- else raise(ActiveRecordError, "No text type has character length #{limit}")
519
- end
520
- else
521
- super
522
- end
523
- end
524
-
525
- def add_column_position!(sql, options)
526
- if options[:first]
527
- sql << " FIRST"
528
- elsif options[:after]
529
- sql << " AFTER #{quote_column_name(options[:after])}"
530
- end
531
- end
532
-
533
- def show_variable(name)
534
- variables = select_all("SHOW VARIABLES LIKE '#{name}'")
535
- variables.first['Value'] unless variables.empty?
536
- end
537
-
538
- def pk_and_sequence_for(table)
539
- keys = []
540
- result = execute("describe #{quote_table_name(table)}")
541
- result.each(:symbolize_keys => true, :as => :hash) do |row|
542
- keys << row[:Field] if row[:Key] == "PRI"
543
- end
544
- keys.length == 1 ? [keys.first, nil] : nil
545
- end
546
-
547
- # Returns just a table's primary key
548
- def primary_key(table)
549
- pk_and_sequence = pk_and_sequence_for(table)
550
- pk_and_sequence && pk_and_sequence.first
551
- end
552
-
553
- def case_sensitive_equality_operator
554
- "= BINARY"
555
- end
556
-
557
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
558
- where_sql
559
- end
560
-
561
- protected
562
- def quoted_columns_for_index(column_names, options = {})
563
- length = options[:length] if options.is_a?(Hash)
564
-
565
- quoted_column_names = case length
566
- when Hash
567
- column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
568
- when Fixnum
569
- column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
570
- else
571
- column_names.map {|name| quote_column_name(name) }
572
- end
573
- end
574
-
575
- def translate_exception(exception, message)
576
- return super unless exception.respond_to?(:error_number)
577
-
578
- case exception.error_number
579
- when 1062
580
- RecordNotUnique.new(message, exception)
581
- when 1452
582
- InvalidForeignKey.new(message, exception)
583
- else
584
- super
585
- end
586
- end
587
-
588
- private
589
- def connect
590
- @connection = Mysql2::Client.new(@config)
591
- configure_connection
592
- end
593
-
594
- def configure_connection
595
- @connection.query_options.merge!(:as => :array)
596
-
597
- # By default, MySQL 'where id is null' selects the last inserted id.
598
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
599
- variable_assignments = ['SQL_AUTO_IS_NULL=0']
600
- encoding = @config[:encoding]
601
-
602
- # make sure we set the encoding
603
- variable_assignments << "NAMES '#{encoding}'" if encoding
604
-
605
- # increase timeout so mysql server doesn't disconnect us
606
- wait_timeout = @config[:wait_timeout]
607
- wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
608
- variable_assignments << "@@wait_timeout = #{wait_timeout}"
609
-
610
- execute("SET #{variable_assignments.join(', ')}", :skip_logging)
611
- end
612
-
613
- # Returns an array of record hashes with the column names as keys and
614
- # column values as values.
615
- def select(sql, name = nil)
616
- execute(sql, name).each(:as => :hash)
617
- end
618
-
619
- def supports_views?
620
- version[0] >= 5
621
- end
622
-
623
- def version
624
- @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
625
- end
626
-
627
- def column_for(table_name, column_name)
628
- unless column = columns(table_name).find { |c| c.name == column_name.to_s }
629
- raise "No such column: #{table_name}.#{column_name}"
630
- end
631
- column
632
- end
633
- end
634
- end
635
- end