mysql2 0.2.24 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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