~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
require 'active_record/connection_adapters/abstract_adapter'
2
 
require 'set'
3
 
 
4
 
module MysqlCompat #:nodoc:
5
 
  # add all_hashes method to standard mysql-c bindings or pure ruby version
6
 
  def self.define_all_hashes_method!
7
 
    raise 'Mysql not loaded' unless defined?(::Mysql)
8
 
 
9
 
    target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
10
 
    return if target.instance_methods.include?('all_hashes') ||
11
 
              target.instance_methods.include?(:all_hashes)
12
 
 
13
 
    # Ruby driver has a version string and returns null values in each_hash
14
 
    # C driver >= 2.7 returns null values in each_hash
15
 
    if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
16
 
      target.class_eval <<-'end_eval'
17
 
      def all_hashes                     # def all_hashes
18
 
        rows = []                        #   rows = []
19
 
        each_hash { |row| rows << row }  #   each_hash { |row| rows << row }
20
 
        rows                             #   rows
21
 
      end                                # end
22
 
      end_eval
23
 
 
24
 
    # adapters before 2.7 don't have a version constant
25
 
    # and don't return null values in each_hash
26
 
    else
27
 
      target.class_eval <<-'end_eval'
28
 
      def all_hashes                                            # def all_hashes
29
 
        rows = []                                               #   rows = []
30
 
        all_fields = fetch_fields.inject({}) { |fields, f|      #   all_fields = fetch_fields.inject({}) { |fields, f|
31
 
          fields[f.name] = nil; fields                          #     fields[f.name] = nil; fields
32
 
        }                                                       #   }
33
 
        each_hash { |row| rows << all_fields.dup.update(row) }  #   each_hash { |row| rows << all_fields.dup.update(row) }
34
 
        rows                                                    #   rows
35
 
      end                                                       # end
36
 
      end_eval
37
 
    end
38
 
 
39
 
    unless target.instance_methods.include?('all_hashes') ||
40
 
           target.instance_methods.include?(:all_hashes)
41
 
      raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
42
 
    end
43
 
  end
44
 
end
45
 
 
46
 
module ActiveRecord
47
 
  class Base
48
 
    # Establishes a connection to the database that's used by all Active Record objects.
49
 
    def self.mysql_connection(config) # :nodoc:
50
 
      config = config.symbolize_keys
51
 
      host     = config[:host]
52
 
      port     = config[:port]
53
 
      socket   = config[:socket]
54
 
      username = config[:username] ? config[:username].to_s : 'root'
55
 
      password = config[:password].to_s
56
 
      database = config[:database]
57
 
 
58
 
      # Require the MySQL driver and define Mysql::Result.all_hashes
59
 
      unless defined? Mysql
60
 
        begin
61
 
          require_library_or_gem('mysql')
62
 
        rescue LoadError
63
 
          $stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.'
64
 
          raise
65
 
        end
66
 
      end
67
 
 
68
 
      MysqlCompat.define_all_hashes_method!
69
 
 
70
 
      mysql = Mysql.init
71
 
      mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
72
 
 
73
 
      default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0
74
 
      options = [host, username, password, database, port, socket, default_flags]
75
 
      ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
76
 
    end
77
 
  end
78
 
 
79
 
  module ConnectionAdapters
80
 
    class MysqlColumn < Column #:nodoc:
81
 
      def extract_default(default)
82
 
        if sql_type =~ /blob/i || type == :text
83
 
          if default.blank?
84
 
            return null ? nil : ''
85
 
          else
86
 
            raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
87
 
          end
88
 
        elsif missing_default_forged_as_empty_string?(default)
89
 
          nil
90
 
        else
91
 
          super
92
 
        end
93
 
      end
94
 
 
95
 
      def has_default?
96
 
        return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
97
 
        super
98
 
      end
99
 
 
100
 
      private
101
 
        def simplified_type(field_type)
102
 
          return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
103
 
          return :string  if field_type =~ /enum/i
104
 
          super
105
 
        end
106
 
 
107
 
        def extract_limit(sql_type)
108
 
          case sql_type
109
 
          when /blob|text/i
110
 
            case sql_type
111
 
            when /tiny/i
112
 
              255
113
 
            when /medium/i
114
 
              16777215
115
 
            when /long/i
116
 
              2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
117
 
            else
118
 
              super # we could return 65535 here, but we leave it undecorated by default
119
 
            end
120
 
          when /^bigint/i;    8
121
 
          when /^int/i;       4
122
 
          when /^mediumint/i; 3
123
 
          when /^smallint/i;  2
124
 
          when /^tinyint/i;   1
125
 
          else
126
 
            super
127
 
          end
128
 
        end
129
 
 
130
 
        # MySQL misreports NOT NULL column default when none is given.
131
 
        # We can't detect this for columns which may have a legitimate ''
132
 
        # default (string) but we can for others (integer, datetime, boolean,
133
 
        # and the rest).
134
 
        #
135
 
        # Test whether the column has default '', is not null, and is not
136
 
        # a type allowing default ''.
137
 
        def missing_default_forged_as_empty_string?(default)
138
 
          type != :string && !null && default == ''
139
 
        end
140
 
    end
141
 
 
142
 
    # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
143
 
    # the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
144
 
    #
145
 
    # Options:
146
 
    #
147
 
    # * <tt>:host</tt> - Defaults to "localhost".
148
 
    # * <tt>:port</tt> - Defaults to 3306.
149
 
    # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
150
 
    # * <tt>:username</tt> - Defaults to "root"
151
 
    # * <tt>:password</tt> - Defaults to nothing.
152
 
    # * <tt>:database</tt> - The name of the database. No default, must be provided.
153
 
    # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
154
 
    # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
155
 
    # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
156
 
    # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
157
 
    # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
158
 
    # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
159
 
    # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
160
 
    #
161
 
    class MysqlAdapter < AbstractAdapter
162
 
 
163
 
      ##
164
 
      # :singleton-method:
165
 
      # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
166
 
      # as boolean. If you wish to disable this emulation (which was the default
167
 
      # behavior in versions 0.13.1 and earlier) you can add the following line
168
 
      # to your environment.rb file:
169
 
      #
170
 
      #   ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
171
 
      cattr_accessor :emulate_booleans
172
 
      self.emulate_booleans = true
173
 
 
174
 
      ADAPTER_NAME = 'MySQL'.freeze
175
 
 
176
 
      LOST_CONNECTION_ERROR_MESSAGES = [
177
 
        "Server shutdown in progress",
178
 
        "Broken pipe",
179
 
        "Lost connection to MySQL server during query",
180
 
        "MySQL server has gone away" ]
181
 
 
182
 
      QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
183
 
 
184
 
      NATIVE_DATABASE_TYPES = {
185
 
        :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
186
 
        :string      => { :name => "varchar", :limit => 255 },
187
 
        :text        => { :name => "text" },
188
 
        :integer     => { :name => "int", :limit => 4 },
189
 
        :float       => { :name => "float" },
190
 
        :decimal     => { :name => "decimal" },
191
 
        :datetime    => { :name => "datetime" },
192
 
        :timestamp   => { :name => "datetime" },
193
 
        :time        => { :name => "time" },
194
 
        :date        => { :name => "date" },
195
 
        :binary      => { :name => "blob" },
196
 
        :boolean     => { :name => "tinyint", :limit => 1 }
197
 
      }
198
 
 
199
 
      def initialize(connection, logger, connection_options, config)
200
 
        super(connection, logger)
201
 
        @connection_options, @config = connection_options, config
202
 
        @quoted_column_names, @quoted_table_names = {}, {}
203
 
        connect
204
 
      end
205
 
 
206
 
      def adapter_name #:nodoc:
207
 
        ADAPTER_NAME
208
 
      end
209
 
 
210
 
      def supports_migrations? #:nodoc:
211
 
        true
212
 
      end
213
 
      
214
 
      def supports_primary_key? #:nodoc:
215
 
        true
216
 
      end
217
 
 
218
 
      def supports_savepoints? #:nodoc:
219
 
        true
220
 
      end
221
 
 
222
 
      def native_database_types #:nodoc:
223
 
        NATIVE_DATABASE_TYPES
224
 
      end
225
 
 
226
 
 
227
 
      # QUOTING ==================================================
228
 
 
229
 
      def quote(value, column = nil)
230
 
        if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
231
 
          s = column.class.string_to_binary(value).unpack("H*")[0]
232
 
          "x'#{s}'"
233
 
        elsif value.kind_of?(BigDecimal)
234
 
          value.to_s("F")
235
 
        else
236
 
          super
237
 
        end
238
 
      end
239
 
 
240
 
      def quote_column_name(name) #:nodoc:
241
 
        @quoted_column_names[name] ||= "`#{name}`"
242
 
      end
243
 
 
244
 
      def quote_table_name(name) #:nodoc:
245
 
        @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
246
 
      end
247
 
 
248
 
      def quote_string(string) #:nodoc:
249
 
        @connection.quote(string)
250
 
      end
251
 
 
252
 
      def quoted_true
253
 
        QUOTED_TRUE
254
 
      end
255
 
 
256
 
      def quoted_false
257
 
        QUOTED_FALSE
258
 
      end
259
 
 
260
 
      # REFERENTIAL INTEGRITY ====================================
261
 
 
262
 
      def disable_referential_integrity(&block) #:nodoc:
263
 
        old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
264
 
 
265
 
        begin
266
 
          update("SET FOREIGN_KEY_CHECKS = 0")
267
 
          yield
268
 
        ensure
269
 
          update("SET FOREIGN_KEY_CHECKS = #{old}")
270
 
        end
271
 
      end
272
 
 
273
 
      # CONNECTION MANAGEMENT ====================================
274
 
 
275
 
      def active?
276
 
        if @connection.respond_to?(:stat)
277
 
          @connection.stat
278
 
        else
279
 
          @connection.query 'select 1'
280
 
        end
281
 
 
282
 
        # mysql-ruby doesn't raise an exception when stat fails.
283
 
        if @connection.respond_to?(:errno)
284
 
          @connection.errno.zero?
285
 
        else
286
 
          true
287
 
        end
288
 
      rescue Mysql::Error
289
 
        false
290
 
      end
291
 
 
292
 
      def reconnect!
293
 
        disconnect!
294
 
        connect
295
 
      end
296
 
 
297
 
      def disconnect!
298
 
        @connection.close rescue nil
299
 
      end
300
 
 
301
 
      def reset!
302
 
        if @connection.respond_to?(:change_user)
303
 
          # See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
304
 
          # reset the connection is to change the user to the same user.
305
 
          @connection.change_user(@config[:username], @config[:password], @config[:database])
306
 
          configure_connection
307
 
        end
308
 
      end
309
 
 
310
 
      # DATABASE STATEMENTS ======================================
311
 
 
312
 
      def select_rows(sql, name = nil)
313
 
        @connection.query_with_result = true
314
 
        result = execute(sql, name)
315
 
        rows = []
316
 
        result.each { |row| rows << row }
317
 
        result.free
318
 
        rows
319
 
      end
320
 
 
321
 
      # Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it.
322
 
      def execute(sql, name = nil) #:nodoc:
323
 
        log(sql, name) { @connection.query(sql) }
324
 
      rescue ActiveRecord::StatementInvalid => exception
325
 
        if exception.message.split(":").first =~ /Packets out of order/
326
 
          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."
327
 
        else
328
 
          raise
329
 
        end
330
 
      end
331
 
 
332
 
      def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
333
 
        super sql, name
334
 
        id_value || @connection.insert_id
335
 
      end
336
 
 
337
 
      def update_sql(sql, name = nil) #:nodoc:
338
 
        super
339
 
        @connection.affected_rows
340
 
      end
341
 
 
342
 
      def begin_db_transaction #:nodoc:
343
 
        execute "BEGIN"
344
 
      rescue Exception
345
 
        # Transactions aren't supported
346
 
      end
347
 
 
348
 
      def commit_db_transaction #:nodoc:
349
 
        execute "COMMIT"
350
 
      rescue Exception
351
 
        # Transactions aren't supported
352
 
      end
353
 
 
354
 
      def rollback_db_transaction #:nodoc:
355
 
        execute "ROLLBACK"
356
 
      rescue Exception
357
 
        # Transactions aren't supported
358
 
      end
359
 
 
360
 
      def create_savepoint
361
 
        execute("SAVEPOINT #{current_savepoint_name}")
362
 
      end
363
 
 
364
 
      def rollback_to_savepoint
365
 
        execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
366
 
      end
367
 
 
368
 
      def release_savepoint
369
 
        execute("RELEASE SAVEPOINT #{current_savepoint_name}")
370
 
      end
371
 
 
372
 
      def add_limit_offset!(sql, options) #:nodoc:
373
 
        if limit = options[:limit]
374
 
          limit = sanitize_limit(limit)
375
 
          unless offset = options[:offset]
376
 
            sql << " LIMIT #{limit}"
377
 
          else
378
 
            sql << " LIMIT #{offset.to_i}, #{limit}"
379
 
          end
380
 
        end
381
 
      end
382
 
 
383
 
 
384
 
      # SCHEMA STATEMENTS ========================================
385
 
 
386
 
      def structure_dump #:nodoc:
387
 
        if supports_views?
388
 
          sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
389
 
        else
390
 
          sql = "SHOW TABLES"
391
 
        end
392
 
 
393
 
        select_all(sql).inject("") do |structure, table|
394
 
          table.delete('Table_type')
395
 
          structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
396
 
        end
397
 
      end
398
 
 
399
 
      def recreate_database(name, options = {}) #:nodoc:
400
 
        drop_database(name)
401
 
        create_database(name, options)
402
 
      end
403
 
 
404
 
      # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
405
 
      # Charset defaults to utf8.
406
 
      #
407
 
      # Example:
408
 
      #   create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
409
 
      #   create_database 'matt_development'
410
 
      #   create_database 'matt_development', :charset => :big5
411
 
      def create_database(name, options = {})
412
 
        if options[:collation]
413
 
          execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
414
 
        else
415
 
          execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
416
 
        end
417
 
      end
418
 
 
419
 
      def drop_database(name) #:nodoc:
420
 
        execute "DROP DATABASE IF EXISTS `#{name}`"
421
 
      end
422
 
 
423
 
      def current_database
424
 
        select_value 'SELECT DATABASE() as db'
425
 
      end
426
 
 
427
 
      # Returns the database character set.
428
 
      def charset
429
 
        show_variable 'character_set_database'
430
 
      end
431
 
 
432
 
      # Returns the database collation strategy.
433
 
      def collation
434
 
        show_variable 'collation_database'
435
 
      end
436
 
 
437
 
      def tables(name = nil) #:nodoc:
438
 
        tables = []
439
 
        result = execute("SHOW TABLES", name)
440
 
        result.each { |field| tables << field[0] }
441
 
        result.free
442
 
        tables
443
 
      end
444
 
 
445
 
      def drop_table(table_name, options = {})
446
 
        super(table_name, options)
447
 
      end
448
 
 
449
 
      def indexes(table_name, name = nil)#:nodoc:
450
 
        indexes = []
451
 
        current_index = nil
452
 
        result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
453
 
        result.each do |row|
454
 
          if current_index != row[2]
455
 
            next if row[2] == "PRIMARY" # skip the primary key
456
 
            current_index = row[2]
457
 
            indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
458
 
          end
459
 
 
460
 
          indexes.last.columns << row[4]
461
 
        end
462
 
        result.free
463
 
        indexes
464
 
      end
465
 
 
466
 
      def columns(table_name, name = nil)#:nodoc:
467
 
        sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
468
 
        columns = []
469
 
        result = execute(sql, name)
470
 
        result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
471
 
        result.free
472
 
        columns
473
 
      end
474
 
 
475
 
      def create_table(table_name, options = {}) #:nodoc:
476
 
        super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
477
 
      end
478
 
 
479
 
      def rename_table(table_name, new_name)
480
 
        execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
481
 
      end
482
 
 
483
 
      def change_column_default(table_name, column_name, default) #:nodoc:
484
 
        column = column_for(table_name, column_name)
485
 
        change_column table_name, column_name, column.sql_type, :default => default
486
 
      end
487
 
 
488
 
      def change_column_null(table_name, column_name, null, default = nil)
489
 
        column = column_for(table_name, column_name)
490
 
 
491
 
        unless null || default.nil?
492
 
          execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
493
 
        end
494
 
 
495
 
        change_column table_name, column_name, column.sql_type, :null => null
496
 
      end
497
 
 
498
 
      def change_column(table_name, column_name, type, options = {}) #:nodoc:
499
 
        column = column_for(table_name, column_name)
500
 
 
501
 
        unless options_include_default?(options)
502
 
          options[:default] = column.default
503
 
        end
504
 
 
505
 
        unless options.has_key?(:null)
506
 
          options[:null] = column.null
507
 
        end
508
 
 
509
 
        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])}"
510
 
        add_column_options!(change_column_sql, options)
511
 
        execute(change_column_sql)
512
 
      end
513
 
 
514
 
      def rename_column(table_name, column_name, new_column_name) #:nodoc:
515
 
        options = {}
516
 
        if column = columns(table_name).find { |c| c.name == column_name.to_s }
517
 
          options[:default] = column.default
518
 
          options[:null] = column.null
519
 
        else
520
 
          raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
521
 
        end
522
 
        current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
523
 
        rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
524
 
        add_column_options!(rename_column_sql, options)
525
 
        execute(rename_column_sql)
526
 
      end
527
 
 
528
 
      # Maps logical Rails types to MySQL-specific data types.
529
 
      def type_to_sql(type, limit = nil, precision = nil, scale = nil)
530
 
        return super unless type.to_s == 'integer'
531
 
 
532
 
        case limit
533
 
        when 1; 'tinyint'
534
 
        when 2; 'smallint'
535
 
        when 3; 'mediumint'
536
 
        when nil, 4, 11; 'int(11)'  # compatibility with MySQL default
537
 
        when 5..8; 'bigint'
538
 
        else raise(ActiveRecordError, "No integer type has byte size #{limit}")
539
 
        end
540
 
      end
541
 
 
542
 
 
543
 
      # SHOW VARIABLES LIKE 'name'
544
 
      def show_variable(name)
545
 
        variables = select_all("SHOW VARIABLES LIKE '#{name}'")
546
 
        variables.first['Value'] unless variables.empty?
547
 
      end
548
 
 
549
 
      # Returns a table's primary key and belonging sequence.
550
 
      def pk_and_sequence_for(table) #:nodoc:
551
 
        keys = []
552
 
        result = execute("describe #{quote_table_name(table)}")
553
 
        result.each_hash do |h|
554
 
          keys << h["Field"]if h["Key"] == "PRI"
555
 
        end
556
 
        result.free
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
 
      private
575
 
        def connect
576
 
          encoding = @config[:encoding]
577
 
          if encoding
578
 
            @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
579
 
          end
580
 
 
581
 
          if @config[:sslca] || @config[:sslkey]
582
 
            @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
583
 
          end
584
 
 
585
 
          @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
586
 
          @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
587
 
          @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
588
 
 
589
 
          @connection.real_connect(*@connection_options)
590
 
 
591
 
          # reconnect must be set after real_connect is called, because real_connect sets it to false internally
592
 
          @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
593
 
 
594
 
          configure_connection
595
 
        end
596
 
 
597
 
        def configure_connection
598
 
          encoding = @config[:encoding]
599
 
          execute("SET NAMES '#{encoding}'") if encoding
600
 
 
601
 
          # By default, MySQL 'where id is null' selects the last inserted id.
602
 
          # Turn this off. http://dev.rubyonrails.org/ticket/6778
603
 
          execute("SET SQL_AUTO_IS_NULL=0")
604
 
        end
605
 
 
606
 
        def select(sql, name = nil)
607
 
          @connection.query_with_result = true
608
 
          result = execute(sql, name)
609
 
          rows = result.all_hashes
610
 
          result.free
611
 
          rows
612
 
        end
613
 
 
614
 
        def supports_views?
615
 
          version[0] >= 5
616
 
        end
617
 
 
618
 
        def version
619
 
          @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
620
 
        end
621
 
 
622
 
        def column_for(table_name, column_name)
623
 
          unless column = columns(table_name).find { |c| c.name == column_name.to_s }
624
 
            raise "No such column: #{table_name}.#{column_name}"
625
 
          end
626
 
          column
627
 
        end
628
 
    end
629
 
  end
630
 
end