mysql2 0.2.0 → 0.2.1

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